--{module_name}_binary_host_mirror
和--{module_name}_binary_site
// .npmrc文件 sass_binary_site=https://npmmirror.com/mirrors/node-sass/ nodejieba_binary_host_mirror=https://npm.taobao.org/mirrors/nodejieba
gyp全称Generate Your Projects
(构建你的项目)
抹平了不同平台之间的差异(比如mac和windows),将源代码编译为可执行的二进制文件。由于某些npm包底层是使用C++/C这种依赖平台的代码,比如node-sass
。所以就不能直接像其他普通包一样从npm中直接下载文件到本地,而是需要将源代码拉下来使用node-gyp
将源代码编译为可执行的二进制文件。
每次安装包都需要使用node-gyp
本地构建,这样很麻烦。库开发者将编译好的各种平台的二进制包发布到node-pre-gyp
中,用户进行install
时会首先根据本地的环境看远端是否有编译好的二进制文件,如果有就直接使用编译好的二进制文件。如果没有则本地使用node-gyp
编译生成本地的可执行二进制文件。
使用package.json
文件的binary
字段指定。
binary: { ... }
--{module_name}_binary_host_mirror
// .npmrc文件 nodejieba_binary_host_mirror=https://npm.taobao.org/mirrors/nodejieba
--{module_name}_binary_host_mirror
使用node-pre-gyp包的就需要使用binary_host_mirror,因为node-pre-gyp内部在install时会优先读取npm config中配置的binary_host_mirror。并且module_name的值为包的package.json
中配置的"binary.module_name"
的值。
以sqlite3
举例:
sqlite3
的package.json
文件
{ "binary": { "module_name": "node_sqlite3", "module_path": "./lib/binding/napi-v{napi_build_version}-{platform}-{libc}-{arch}", "host": "https://github.com/TryGhost/node-sqlite3/releases/download/", "remote_path": "v{version}", "package_name": "napi-v{napi_build_version}-{platform}-{libc}-{arch}.tar.gz", "napi_versions": [ 3, 6 ] }, "scripts": { // .... "install": "node-pre-gyp install --fallback-to-build", //... }, }
执行npm i sqlite3
时会首先从npm
中下载sqlite3
的源代码,下载完后检测到源码的package.json
中的script
标签中有install
指令,就会执行install
,执行node-pre-gyp install --fallback-to-build
。node-pre-gyp
在node_modules/.bin/node-pre-gyp
中,执行node-pre-gyp
的lib/main.js
文件。
这里的install命令在js中可以使用process.argv[2]
和process.argv[3]
分别读取到install
和--fallback-to-build
。process.argv[0]
的值为nodejs可执行文件所在的位置,process.argv[1]
的值为当前执行的 JavaScript 文件所在的路径。从2开始都是命令行参数了。
node-pre-gyp
中下载预编译二进制文件的代码
const opts = { // ... module_name: package_json.binary.module_name, } const validModuleName = opts.module_name.replace('-', '_'); const host = process.env['npm_config_' + validModuleName + '_binary_host_mirror'] || package_json.binary.host;
可以通过process.env
读取npm的config,比如读取registry
:process.env.npm_config_registry
所以这里的host优先从npm config
里面去读binary_host_mirror
,其中的module_name
的值为包的package.json
文件中的"binary.module_name"
字段。
--{module_name}_binary_site
node-sass
的package.json
文件:{ "nodeSassConfig": { "binarySite": "https://github.com/sass/node-sass/releases/download" }, "scripts": { // ... "install": "node scripts/install.js", // ... }, }
使用npm i node-sass
安装node-sass
时会先从npm中下载node-sass
的源码。源码下载完成后检测到script
中有install
命令,如何执行install
命令。
scripts/install.js
文件
function checkAndDownloadBinary() { var cachedBinary = sass.getCachedBinary(), cachePath = sass.getBinaryCachePath(), binaryPath = sass.getBinaryPath(); // 下载二进制可执行文件 download(sass.getBinaryUrl(), binaryPath, function(err) { // .... }); } // If binary does not exist, download it checkAndDownloadBinary();
调用checkAndDownloadBinary方法进行下载预编译文件。
getBinaryUrl
和getArgument
函数:
function getArgument(name, args) { var flags = args || process.argv.slice(2), index = flags.lastIndexOf(name); if (index === -1 || index + 1 >= flags.length) { return null; } return flags[index + 1]; } 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('/'); }
下载预编译文件时优先使用在命令行中指定的--sass-binary-site
。比如:npm install node-sass --sass-binary-site=https://npmmirror.com/mirrors/node-sass/
其次使用环境变量中指定的SASS_BINARY_SITE
。比如:export SASS_BINARY_SITE=http://example.com/
再然后就是读取npm配置(常用的是.npmrc
文件)中的sass_binary_site
,通过process.env.npm_config_sass_binary_site
读取。
再然后读取package.json
中配置的binarySite
字段。
{ // ... "nodeSassConfig": { "binarySite": "https://github.com/sass/node-sass/releases/download" }, // ... }
最后就是一个写死的github地址'https://github.com/sass/node-sass/releases/download'
。
要安装的包如果使用了node-pre-gyp
(比如nodejieba
),在.npmrc
文件中配置国内预编译文件镜像URL就需要使用--{module_name}_binary_host_mirror
。其中的module_name
的值为包的package.json
中配置的"binary.module_name"
的值。
什么时候使用--{module_name}_binary_site
实际是由要安装的包内部自己实现的,典型的案例就是node-sass
。