背景
上一篇 搭建 NPM Proxy 成功搭建了一个 NPM 代理,但很快就又遇到问题了:node-sass 这种典型的有预编译包,而且需要到 GitHub 下载的 Package,安装就会失败。
寻找原因
我们的 NPM Proxy 已经将所有包重定向至 taobao registry,tarball(源码包)也完全是从 taobao 源下载的,然后我根据 cnpm 的首页例子尝试:
alias cnpm="npm --registry=https://registry.npm.taobao.org \
--cache=$HOME/.npm/.cache/cnpm \
--disturl=https://npm.taobao.org/dist \
--userconfig=$HOME/.cnpmrc"
发现依然会从 GitHub 下载 预编译 包,而且直接下载 taobao 的 tarball 发现跟官方源的是完全一样的。再进而直接使用 cnpm
客户端,发现是从taobao 源下载,并且安装成功。
那我们来研究一下 cnpm
是怎么做到的,看了一下源码:https://github.com/cnpm/cnpm/blob/master/lib/origin_npm.js,cnpm
的安装不是直接调用 npm
而是调用了 npminstall 这一个工具。
在调用安装入口前发现:bin/install.js#L213,读取了配置里面的镜像地址,然后设置为环境变量:
// https://github.com/cnpm/npminstall/blob/master/lib/config.js
const config = {
env: {
// show node-pre-gyp http info
// like "node-pre-gyp http GET https://npm.taobao.org/mirrors/fsevents/v1.0.6/fse-v1.0.6-node-v46-darwin-x64.tar.gz"
npm_config_loglevel: 'http',
},
chineseMirrorUrl: 'https://npm.taobao.org/mirrors',
chineseRegistry: 'https://registry.npm.taobao.org',
};
// https://github.com/cnpm/npminstall/blob/master/bin/install.js#L213
if (inChina) {
binaryMirrors = yield utils.getBinaryMirrors(registry, { proxy });
if (customChinaMirrorUrl) {
for (const key in binaryMirrors) {
const item = binaryMirrors[key];
if (item.host) {
item.host = item.host.replace(globalConfig.chineseMirrorUrl, customChinaMirrorUrl);
}
}
}
// set env
for (const key in binaryMirrors.ENVS) {
env[key] = binaryMirrors.ENVS[key];
if (customChinaMirrorUrl) {
env[key] = env[key].replace(globalConfig.chineseMirrorUrl, customChinaMirrorUrl);
}
}
}
再配合 tarball 源码包,如 node-sass
:
- 每次安装的时候会调用
scripts/install.js
install
脚本里调用了getBinaryUrl
这个函数- 函数里面定义了获取 预编译包的地址
function getBinaryUrl() {
var site = getArgument('--sass-binary-site') ||
process.env.SASS_BINARY_SITE ||
process.env.npm_config_sass_binary_site ||
(pkg.nodeSassConfig && pkg.nodeSassConfig.binarySite) ||
'https://github.com/sass/node-sass/releases/download';
return [site, 'v' + pkg.version, getBinaryName()].join('/');
}
总结
到此,总结一下:
- 执行
cnpm
命令 - 调用
npminstall
库 - 根据配置文件的镜像地址,设置对应包的环境变量
- 从 registry 下载源码包
- 执行源码包的安装脚本
- 安装脚本获取预编译包的地址
解决
那怎么解决呢?
一、设置 taobao 的镜像代理
location / {
proxy_pass https://npm.taobao.org/mirrors/;
}
二、解决重定向
但是问题又来了,taobao 镜像里面的包地址使用了 302
跳转到了 http://cdn.npm.taobao.org/dist
,这时候我想起了上一篇提到的在 nginx
内部重定向的方法:
server {
location / {
proxy_pass https://npm.taobao.org/mirrors/;
proxy_intercept_errors on;
error_page 302 = @handle_redirect;
}
location @handle_redirect {
set $saved_redirect_location '$upstream_http_location';
proxy_pass http://cdn.npm.taobao.org/dist/;
}
}
备注:但是好像直接将 proxy_pass
设置到 CDN 地址也是可以的。
二、安装前,预先设定好会有预编译包的环境变量
export SASS_BINARY_SITE=http://mirror.internal/npm/mirrors/node-sass