前端架构师-week4-封装通用的npm包管理类Package

这篇具有很好参考价值的文章主要介绍了前端架构师-week4-封装通用的npm包管理类Package。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

脚手架命令本地调试功能支持

动态执行库exec模块创建

创建 npm 模块通用类 Package

Package 类的属性、方法定义及构造函数逻辑开发

Package 类获取入口文件路径功能开发(pkg-dir应用+解决不同操作系统路径兼容问题) 

利用 npminstall 库安装 npm 模块

Package 类判断模块是否存在方法开发

Package 类更新模块逻辑开发

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

总结


脚手架命令本地调试功能支持

        环境变量是操作系统级别的,可以在Node多进程之间共享。做业务逻辑解耦。

        监听可以在业务逻辑执行之前执行。

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

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

  program.parse(process.argv);
}


// imooc-cli-dev/commands/init/index.js 文件内容

'use strict'

function init(projectName, cmdObj) {
    console.log('init', projectName, cmdObj.force, process.env.CLI_TARGET_PATH)
}

module.exports = init

动态执行库exec模块创建

lerna create @imooc-cli-dev/exec

entry point: (lib/exec.js) lib/index.js #记得修改入口文件名
// 在 core/cli/package.json 中注册

"dependencies": {
    "@imooc-cli-dev/exec": "file:../exec",
    "colors": "^1.4.0"
}


// 在 core/cli 命令行中执行
npm link
// core/exec/index.js 文件内容

'use strict';

module.exports = exec;

function exec() {
    console.log('exec')
    console.log(process.env.CLI_TARGET_PATH)
    console.log(process.env.CLI_HOME_PATH)
}

创建 npm 模块通用类 Package

1. targetPath -> modulePath

2. modulePath -> Package ( npm 模块 )

3. Package.getRootFile(获取入口文件),无需把此步代码暴露在 exec 中

4. Package.update / Package.install

5. 封装 -> 复用

实现步骤:

lerna create @imooc-cli-dev/package

entry point: (lib/package.js) lib/index.js  #记得修改入口文件名

#把 package 移到 imooc-cli-dev/models 中
// 在 core/exec/package.json 中注册

"dependencies": {
    "@imooc-cli-dev/package": "file:../../models/package"
}


// 在 core/exec 命令行中执行
npm install
// models/package/lib/index.js 文件内容

'use strict'

class Package {
    constructor() {
        console.log('Package constructor')
    }
}

module.exports = Package
// core/exec/lib/index.js 中引用 package

const Package = require('@imooc-cli-dev/package')

function exec() {
    const pkg = new Package()
}

module.exports = exec

Package 类的属性、方法定义及构造函数逻辑开发

小知识点:

        lerna bootstrap 相当于 npm install 

        可变参数通过 arguments 来获取

         log.verbose() 调试时打印,生产环境不打印

        exec 引用 package,cli 模块也要进行 npm link

function isObject(o) {
  return Object.prototype.toString.call(o) === '[object Object]';
}
// models/package/lib/index.js 文件内容

'use strict';

const { isObject } = require('@imooc-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.storeDir = options.storeDir;
    // package的name
    this.packageName = options.packageName;
    // package的version
    this.packageVersion = options.packageVersion;
  }

  // 判断当前Package是否存在
  async exists() {
  }

  // 安装Package
  async install() {
  }

  // 更新Package
  async update() {
  }

  // 获取入口文件的路径
  getRootFilePath() {
  }
}

module.exports = Package;
// core/exec/lib/index.js 文件内容

'use strict';

const Package = require('@imooc-cli-dev/package');
const log = require('@imooc-cli-dev/log');

const SETTINGS = {
  init: '@imooc-cli/init'
};


async function exec() {
  let targetPath = process.env.CLI_TARGET_PATH;
  const homePath = process.env.CLI_HOME_PATH;
  log.verbose('targetPath', targetPath);
  log.verbose('homePath', homePath);

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

 
  pkg = new Package({
    targetPath,
    packageName,
    packageVersion,
  });
  console.log(pkg)
}

module.exports = exec;

Package 类获取入口文件路径功能开发(pkg-dir应用+解决不同操作系统路径兼容问题) 

思路: 

  获取入口文件的路径
  getRootFilePath() {

    1. 获取 package.json 所在目录 - pkg-dir
    2. 读取 package.json - require()
    3. 寻找main/lib - path
    4. 路径的兼容(macOS/windows)(macOS的path.sep路径分隔符为/,windows为\)
  }

步骤: 

# imooc-cli-dev/models/package 命令行中执行
npm install -S pkg-dir
const path = require('path');
const pkgDir = require('pkg-dir').sync;
const formatPath = require('@imooc-cli-dev/format-path');

// 获取入口文件的路径
  getRootFilePath() {
      // 1. 获取package.json所在目录
      const dir = pkgDir(targetPath);
      if (dir) {
        // 2. 读取package.json
        const pkgFile = require(path.resolve(dir, 'package.json'));
        // 3. 寻找main/lib
        if (pkgFile && pkgFile.main) {
          // 4. 路径的兼容(macOS/windows)
          return formatPath(path.resolve(dir, pkgFile.main));
        }
      }
      return null;
  }
# 在 imooc-cli-dev/utils 下新建包,修改入口文件名为 index.js
lerna create @imooc-cli-dev/format-path
// 在 models/package 中注册 utils/format-path
"dependencies": {
    "@imooc-cli-dev/format-path": "file:../../utils/format-path"
}

// 在 models/package 命令行中执行
npm install
// utils/format-path/lib/index.js 文件内容

'use strict';

const path = require('path');

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

利用 npminstall 库安装 npm 模块

        这节实现 package 模块最重要的方法,安装依赖。

npminstall 库作用:

        Make npm install fast and handy.

// Usage:

const npminstall = require('npminstall');
const path = require('path');
const userHome = require('user-home');

npminstall({
  root: path.resolve(userHome, '.imooc-cli-dev'),
  storeDir: path.resolve(userHome, '.imooc-cli-dev', 'node_modules'),
  registry: 'https://registry.npmjs.org',
  pkgs: [  //要安装的包名
    { name: 'foo', version: '~1.0.0' },
  ],
});

// 结果:会在 user/.imooc-cli-dev/node_modules 中,安装 foo package

代码实现: 

npm install -S npminstall
// models/package/lib/index.js 文件内容

const npminstall = require('npminstall');
const { getDefaultRegistry, getNpmLatestVersion } = require('@imooc-cli-dev/get-npm-info');

class Package {

  // 安装Package
  async install() {

    return npminstall({
      root: this.targetPath,
      storeDir: this.storeDir,
      registry: getDefaultRegistry(),
      pkgs: [{
        name: this.packageName,
        version: this.packageVersion,
      }],
    });
  }
}
// models/package/package.json 文件中注册 get-npm-info
"dependencies": {
    "@imooc-cli-dev/get-npm-info": "file:../../utils/get-npm-info"
}

// models/package 命令行中执行
npm install
// core/exec/lib/index.js 文件内容

'use strict';

const path = require('path');
const Package = require('@imooc-cli-dev/package');
const log = require('@imooc-cli-dev/log');

const SETTINGS = {
  init: '@imooc-cli/init'
};

const CACHE_DIR = 'dependencies';

async function exec() {
  let targetPath = process.env.CLI_TARGET_PATH;
  const homePath = process.env.CLI_HOME_PATH;
  let storeDir = '';
  let pkg;
  log.verbose('targetPath', targetPath);
  log.verbose('homePath', homePath);

  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');
    log.verbose('targetPath', targetPath);
    log.verbose('storeDir', storeDir);
    pkg = new Package({
      targetPath,
      storeDir,
      packageName,
      packageVersion,
    });
    if (await pkg.exists()) {
      // 更新package
    } else {
      // 安装package
      await pkg.install();
    }
  } else {
    pkg = new Package({
      targetPath,
      packageName,
      packageVersion,
    });
  }
  const rootFile = pkg.getRootFilePath();
  if (rootFile) {
    require(rootFile).call(null, arguments});
  }
}

module.exports = exec;

Package 类判断模块是否存在方法开发

npm install path-exists -S
// models/package/lib/index.js 文件内容

const pathExists = require('path-exists').sync;
const { getDefaultRegistry, getNpmLatestVersion } = require('@imooc-cli-dev/get-npm-info');

class Package {
  constructor(options) {
    ......
    // package的缓存目录前缀
    this.cacheFilePathPrefix = this.packageName.replace('/', '_');
  }

  //安装时可以安装 latest,查询路径时还是要具体到版本号的。 
  //_@imooc-cli_init@1.1.2@@imooc-cli/ 磁盘上文件名格式
  //@imooc-cli/init 1.1.2
  get cacheFilePath() {
    return path.resolve(this.storeDir, `_${this.cacheFilePathPrefix}@${this.packageVersion}@${this.packageName}`);
  }

  async prepare() {
    if (this.packageVersion === 'latest') {
      this.packageVersion = await getNpmLatestVersion(this.packageName);
    }
  }

  // 判断当前Package是否存在
  async exists() {
    if (this.storeDir) {
      await this.prepare();
      return pathExists(this.cacheFilePath);
    } else {
      return pathExists(this.targetPath);
    }
  }

  // 安装Package
  async install() {
    await this.prepare();
    return npminstall({
      ......
    });
  }
}
// utils/get-npm-info/lib/index.js 文件内容

async function getNpmLatestVersion(npmName, registry) {
  let versions = await getNpmVersions(npmName, registry);
  if (versions) {
    return versions.sort((a, b) => semver.gt(b, a))[0];
  }
  return null;
}

Package 类更新模块逻辑开发

fs-extra 库作用: 

        fs-extra adds file system methods that aren't included in the native fs module and   adds promise support to the fs methods. It also uses graceful-fs to prevent EMFILE errors.     It should be a drop in replacement for fs.

fs-extra methods:

Async
copy emptyDir ensureFile ensureDir ensureLink ensureSymlink mkdirp
mkdirs move outputFile outputJson pathExists readJson remove writeJson 
Sync
copySync emptyDirSync ensureFileSync ensureDirSync ensureLinkSync
ensureSymlinkSync mkdirpSync mkdirsSync moveSync outputFileSync
outputJsonSync pathExistsSync readJsonSync removeSync writeJsonSync

fs-extra Usage: 

const fs = require('fs-extra')

// Async with promises:
fs.copy('/tmp/myfile', '/tmp/mynewfile')
  .then(() => console.log('success!'))
  .catch(err => console.error(err))

// Async with callbacks:
fs.copy('/tmp/myfile', '/tmp/mynewfile', err => {
  if (err) return console.error(err)
  console.log('success!')
})

// Sync:
try {
  fs.copySync('/tmp/myfile', '/tmp/mynewfile')
  console.log('success!')
} catch (err) {
  console.error(err)
}

// Async/Await:
async function copyFiles () {
  try {
    await fs.copy('/tmp/myfile', '/tmp/mynewfile')
    console.log('success!')
  } catch (err) {
    console.error(err)
  }
}

步骤和实现代码: 

const fse = require('fs-extra'); //先 npm install -S

class Package {

  ......

  async prepare() {
    if (this.storeDir && !pathExists(this.storeDir)) {   //新增
      fse.mkdirpSync(this.storeDir);    //把没有创建好的目录都给创建好
    }
    if (this.packageVersion === 'latest') {
      this.packageVersion = await getNpmLatestVersion(this.packageName);
    }
  }

  getSpecificCacheFilePath(packageVersion) {
    return path.resolve(this.storeDir, `_${this.cacheFilePathPrefix}@${packageVersion}@${this.packageName}`);
  }
  
  // 更新Package
  async update() {
    await this.prepare();
    // 1. 获取最新的npm模块版本号
    const latestPackageVersion = await getNpmLatestVersion(this.packageName);
    // 2. 查询最新版本号对应的路径是否存在
    const latestFilePath = this.getSpecificCacheFilePath(latestPackageVersion);
    // 3. 如果不存在,则直接安装最新版本
    if (!pathExists(latestFilePath)) {
      await npminstall({
        root: this.targetPath,
        storeDir: this.storeDir,
        registry: getDefaultRegistry(),
        pkgs: [{
          name: this.packageName,
          version: latestPackageVersion,
        }],
      });
      this.packageVersion = latestPackageVersion; //健壮性体现,注意
    } else {
      this.packageVersion = latestPackageVersion; //健壮性体现,注意
    }
  }
}

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

class Package {

  // 获取入口文件的路径
  getRootFilePath() {
    function _getRootFile(targetPath) {
      // 1. 获取package.json所在目录
      const dir = pkgDir(targetPath);
      if (dir) {
        // 2. 读取package.json
        const pkgFile = require(path.resolve(dir, 'package.json'));
        // 3. 寻找main/lib
        if (pkgFile && pkgFile.main) {
          // 4. 路径的兼容(macOS/windows)
          return formatPath(path.resolve(dir, pkgFile.main));
        }
      }
      return null;
    }
    if (this.storeDir) {    //新增判断项,命令行中未传入targetPath
      return _getRootFile(this.cacheFilePath);
    } else {
      return _getRootFile(this.targetPath);
    }
  }
}

总结

        我们已经把 package 类封装好了。代表一个npm模块。支持直接使用本地已有的代码文件,和缓存npm库里面的文件两种功能。实现了 npm 模块安装、更新、判断存在、获取入口文件路径等一系列逻辑。对class也有比较多的应用,构造函数、get、方法。逻辑还是比较复杂的。 

        此章我们共同完成了 imooc-cli 脚手架动态命令加载 的功能,这块功能的难度还是相当大的。我们实现了一整套动态加载的机制,我们将动态命令抽象为一个package,既可以通过缓存去实时从 npm 下载这个命令,也可以直接从本地去映射这个命令。核心是获取一个入口文件地址,将这个入口文件直接以 require 方式进行调用。我们还差一步,就是将 require 方式调用改为 node 子进程方式调用。可以额外获得更多的 CPU 资源,以便获得更高的执行性能。文章来源地址https://www.toymoban.com/news/detail-435386.html

到了这里,关于前端架构师-week4-封装通用的npm包管理类Package的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 安全与认证Week4

    目录 目录 Web Security (TLS/SSL) 各层安全协议 Transport Layer Security (TLS)传输层安全性(TLS) SSL和TLS的联系与区别 TLS connectionsession 连接与会话 题目2答案点 TLS ArchitectureTLS架构(5个协议) 题目1答案点 Handshake Protocol(握手协议) 其它几个协议,包括后面的示例 题目10答案点 Handshake

    2024年02月02日
    浏览(30)
  • 基于VUE3+Layui从头搭建通用后台管理系统(前端篇)八:自定义组件封装上

      本章实现一些自定义组件的封装,包括数据字典组件的封装、下拉列表组件封装、复选框单选框组件封装、单选框组件封装、文件上传组件封装、级联选择组件封装、富文本组件封装等。 1. 详细课程地址: https://edu.csdn.net/course/detail/38183 2. 源码下载地址: 点击下载

    2024年02月12日
    浏览(36)
  • HGame 2023 Week4 部分Writeup

    文章同时发布于我的博客:https://blog.vvbbnn00.cn/archives/hgame2023week4-bu-fen-writeup 第四周的比赛难度较高,同时也出现了不少颇为有趣的题目。可惜笔者比较菜,做出来的题目数量并不是很多,不过里面确实有几道题值得好好讲讲。不多废话了,抓紧端上来吧(喜)。 注:本周C

    2024年02月03日
    浏览(38)
  • NewStarCTF 2023 公开赛道 WEEK4|CRYPTO 部分WP

    1、题目信息 提示: \\\"Schmidt Samoa\\\" 附件信息 2、解题方法 学了一个新技巧,哈哈哈。 简介 : Schmidt-Samoa密码系统,像rabin加密一样,其安全性基于整数因式分解的难度。但 Rabin 解密时会得到四个解,而 Schmidt-Samor 得到的是唯一解。 密钥生成 1.选取两个大的质数p和q并进行计算

    2024年02月08日
    浏览(37)
  • 0xGame week4-WEB wp

    完结撒花!!!学到了很多很多,算是我这个WEB菜鸡过渡期的一个见证吧。 0xGame虽然也没做出来几道(大嘘),但是 一步步跟着复现也学了很多好玩的知识点和思路,希望下次能进化成WEBak哥hhhhhh~~~~ 来看最后一周,全是java框架,麻了。 整体不难,hint把解题方法基本写脸上

    2024年02月05日
    浏览(37)
  • [BUUCTF NewStarCTF 2023 公开赛道] week4 crypto/pwn

    再补完这个就基本上完了. Schmidt-Samoa密码系统看上去很像RSA,其中N=pqq, 给的e=N给了d NTRU又一个格的基本应用   当E.order() == p时   p-1光滑时的分解 此题先是p-1光滑分解,然后是e=3*0x10000先求3次根再用rabin求16次    求误差,虽然被分成3个数组,但本质上是一个,可以连到一起求解. 

    2024年02月07日
    浏览(30)
  • 剑指offer题解合集——Week4day2

    题目链接:二叉树中和为某一值的路径 AC代码 思路: 整体思路

    2024年01月15日
    浏览(38)
  • 【北邮国院大三下】Cybersecurity Law 网络安全法 Week4

    北邮国院大三电商在读,随课程进行整理知识点。仅整理PPT中相对重要的知识点,内容驳杂并不做期末突击复习用。个人认为相对不重要的细小的知识点不列在其中。如有错误请指出。转载请注明出处,祝您学习愉快。 编辑软件为Effie,如需要pdf/docx/effiesheet/markdown格式的文件

    2024年02月11日
    浏览(60)
  • NEUQ-acm第二期训练Week4——代码源div2

    RSA算法选择两个不同质数的积作为模数。现在有两个正整数 A,B,如果它们是不同的质数,则判定为 full credit;否则,如果A⋅B不是任意大于1的整数的平方的整数倍,则判定 partial credit;否则判定为no credit。 一行两个正整数 A,B。 full credit 或 partial credit 或 no credit。 数据规模

    2024年02月16日
    浏览(27)
  • NewStarCTF2023week4-midsql(利用二分查找实现时间盲注攻击)

    大致测试一下,发现空格被过滤了 使用内联注释/**/绕过,可行 使用%a0替代空格,也可以  再次测试发现等号也被过滤,我们使用 like 代替 (我最开始以为是and被过滤,并没有,如果是and或者or被过滤我们也可以使用 和 || 替代) 但是这里尝试了很多都只返回一个页面,没有

    2024年02月07日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包