记 node_modules 版本锁定方案的落地-阿里云开发者社区

开发者社区> ruuui> 正文

记 node_modules 版本锁定方案的落地

简介: # 背景 半年前开始关注 `npm shrinkwrap` ,因为项目里每次 `npm install` 都会出现因依赖库版本不一致导致的构建问题。 当时的默认安装工具是 tnpm,由于 tnpm 从 cnpm 来,cnpm 又通过 npminstall 实现,而 npminstall 又不支持 `shrinkwrap` ,无奈只能考虑通过 npm 方式进行安装。
+关注继续查看

背景

半年前开始关注 npm shrinkwrap ,因为项目里每次 npm install 都会出现因依赖库版本不一致导致的构建问题。

当时的默认安装工具是 tnpm,由于 tnpm 从 cnpm 来,cnpm 又通过 npminstall 实现,而 npminstall 又不支持 shrinkwrap ,无奈只能考虑通过 npm 方式进行安装。

官方 npm 没有 @ali 依赖,必须使用 --registry=http://registry.npm.alibaba-inc.com 指定资源仓库。

对于一般的项目,设置一个别名即可解决这个问题:

alias alinpm='npm --registry=http://registry.npm.alibaba-inc.com'
# 或 cnpm
alias alinpm='npm --registry=https://registry.npm.taobao.org'

但问题又来了,本地项目中有一模块 @ali/imagemin 依赖以下这些模块:

  • advpng-bin
  • jpeg-recompress-bin
  • jpegtran-bin
  • optipng-bin
  • pngcrush-bin
  • pngquant-bin

这些模块安装时会从 github 下载执行文件,具体流程如下:

image.png

因为有两处逻辑涉及下载,而下载地址又是 github cdn。国内环境不出意外的话一定被墙,所以 npm 安装方式行不通,安装到 pngquant 时会报 pngquant pre-build test failed。之后走源码构建逻辑又从 github 下载,继续报 pngquant failed to build

image.png

但奇怪的是 tnpm 安装却一切正常:

image.png

为什么

非常有意思,扒扒 npminstall 源码跟踪安装流程,发现 bin/install.js 脚本中有一段:

// if in china, will automatic using chines registry and mirros.
const inChina = argv.china || !!process.env.npm_china;
// if exists, override default china mirror url
const customChinaMirrorUrl = argv['custom-china-mirror-url'];

顺着 customChinaMirrorUrl 找到了:

image.png

这段代码表示从这几处资源仓库里找 binary-mirror-config 模块的最新版本,下载后返回mirrors.china。搜一下,发现 npminstall 用了一个比较鸡贼的办法:

image.png

case by case 的把所有需要下载的二进制全做了一次镜像!

顺藤摸瓜

抑制不住兴奋继续往下扒,看看在哪里做了处理:

yield installLocal(config);
 + require('./local_install')
  + _install()
   + installOne()
    + install()
     + _install()
      + download()
       + npm()
        + download()

终于在 lib/download/npm.js 的第 238 行看到了 pngquant-bin

// use mirror url instead
// e.g.: pngquant-bin
const indexFilepath = path.join(ungzipDir, 'lib/index.js');
yield replaceHostInFile(pkg, indexFilepath, binaryMirror, options);
const installFilepath = path.join(ungzipDir, 'lib/install.js');
yield replaceHostInFile(pkg, installFilepath, binaryMirror, options);

npminstall 在下载流程里单独处理了所有需要镜像的二进制执行文件,找到一处匹配就 replace 其 binary host,起到镜像的效果。

解决它

找到关键点就好办了,目前有两种方案:

  1. 单独为每个模块的 package.json 添加 postinstall 脚本
  2. 通过 npm 钩子 hooks script 对所有模块单独进行处理

修改别人的模块显然不可能,那就只能用方案2了

编写脚本

要使 hooks script 起作用,得在 node_modules 目录里创建一个 .hooks 目录,里面存放着以「事件名称」命名的脚本文件(安装脚本请参见:npm scripts

project_dir
 + node_modules
  + .hooks
   + preinstall <---

在 preinstall 脚本里可以使用 process.env.npm_package_name 获得当前安装的模块名称,伪代码如下:

#!/usr/bin/env node
if('pngquant-bin' === process.env.npm_package_name){
    const PWD = process.env.PWD;
    replaceHostInFile(path.join(PWD, 'lib/index.js'));
    replaceHostInFile(path.join(PWD, 'lib/install.js'));
}
function replaceHostInFile(filepath) {
    const exists = fs.existsSync(filepath);
    if (!exists) return;
    let content = fs.readFileSync(filepath, 'utf8');
    content = content.replace(/\/\/raw\.github\.com/, '//raw.github.cnpmjs.org');
    content = content.replace(/\/\/github\.com/, '//github.com.cnpmjs.org');
    fs.writeFileSync(filepath, content);
}

执行结果:

image.png

到此,问题已完全解决,安装上最新的 npm 5.x ,轻松使用 package-lock.json 提供的版本锁定特性。

更进一步

实际使用中不可能每个项目都复制一份 hooks scripts,要脱离 case by case 必须自动化。

不细讲,以下是最终版本,欢迎大家试用:

hook-binary-mirror

用法

  1. 全局安装 hook-binary-mirror 模块
# tnpm 方式
tnpm --by=npm i hook-binary-mirror -g
# npm 方式
npm --registry=https://registry.npm.taobao.org i hook-binary-mirror -g
  1. 删除原有的 node_modules 目录
cd project_dir
rm -rf node_modules
  1. 为 package.json 增加一处 scripts
"scripts": {
  "preinstall": "hook-binary-mirror"
}

完成

结束

困扰了半年多的问题终于解决,此项任务终于可以放心的置为「已结束」。过程中翻阅了 npminstall 的源码,获益良多。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
从网上找的 visual studio 的各个版本下载地址,vs2010/vs2012/vs2013带注册码
从网上找的 visual studio 的各个版本下载地址,很全,从 6.0 一直 到 vs2013,要的拿去吧。。。Microsoft Visual Studio 6.0 下载:英文版360云盘下载:http://l11.yunpan.cn/lk/sVeBLC3bhumrI英文版115网盘下载:http://115.com/file/bew2qrau英文版迅雷下载:http://61.138
6546 0
git 本地项目推送到远程仓库(或者仓库分支)Window版-01
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qingfeng812/article/details/51382521 ...
1073 0
CentOS 6.5安装二进制版本的MongoDB 2.6
CentOS 6.5安装二进制版本的MongoDB 2.6 MongoDB的国外镜像访问非常慢,以至于选择MongoDB官网的在线安装很不靠谱。那么,我们可以选择安装MongoDB 2.6的二进制发布包。
781 0
会员卡开卡表单模板配置(alipay.marketing.card.formtemplate.set)JAVA版本demo
官方接口文档:[url]https://docs.open.alipay.com/251/105668/[/url],我这里写的是开卡组件前两步的操作流程,仅供参考 1.调用接口前的准备工作(创建应用等)参考该文档:[url]https://docs.
737 0
JAVA动态代理(JDK版本)
在代理设计模式里,代理类扮演桥接使用方和实现方的角色。使用者通过代理类获得实现类的访问权限,并通过代理类定制执行业务逻辑前、后的处理流程。
2126 0
ArcGIS 客户端跨版本连接Oracle 地理数据库时的兼容性说明
很多用户都会有这样的疑问: 1:我是否可以使用ArcGIS Desktop9.3连接ArcSDE10 2:我是否可以使用ArcGIS 10.
945 0
+关注
ruuui
前端开发者
3
文章
2
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载