搭建 NPM Proxy 之预编译包

背景

上一篇 搭建 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.jscnpm 的安装不是直接调用 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

  1. 每次安装的时候会调用 scripts/install.js
  2. install 脚本里调用了 getBinaryUrl 这个函数
  3. 函数里面定义了获取 预编译包的地址
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('/');
}

总结

到此,总结一下:

  1. 执行 cnpm 命令
  2. 调用 npminstall
  3. 根据配置文件的镜像地址,设置对应包的环境变量
  4. 从 registry 下载源码包
  5. 执行源码包的安装脚本
  6. 安装脚本获取预编译包的地址

解决

那怎么解决呢?

一、设置 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
2019-03-10 15:00