OverRainbow

How to treat lock file

☕️ 1 min read

你可以从本文了解到

问题背景

最近taobao npm registry下线,导致之前lock文件里显式依赖taobao源的通通爆炸了,这是本文的写作背景。 我们的目标就是让lock文件能切换到其他正常工作的源,比如npmjs。

原因与解决方案

lock文件如何记录源及如何修复

我们知道lock文件主要的作用是记录依赖及其解析结果(版本、散列值等),从而加速依赖安装速度。那么对于每一条解析结果,我们可以看到下面这个例子,其中很显著的一个标志就是【resolved "解析的源地址/包信息"】。

"@babel/core@7.4.3":
  version "7.4.3"
  resolved "https://registry.npm.taobao.org/@babel/core/download/@babel/core-7.4.3.tgz#198d6d3af4567be3989550d97e068de94503074f"  integrity sha1-GY1tOvRWe+OYlVDZfgaN6UUDB08=
  dependencies:
    "@babel/code-frame" "^7.0.0"
    "@babel/generator" "^7.4.0"
    "@babel/helpers" "^7.4.3"
    "@babel/parser" "^7.4.3"
    "@babel/template" "^7.4.0"

由于不同的源它的包信息都是一样的,因此,我们只需要把这些resolved的实效的内容通过字符串匹配的方式替换掉就行了。

直接手动披露替换也可以,但是呢,有人已经写了一个locktt工具专门做这事。

使用方法也很简单:

npm i -g @kie/lock-treatment-tool // 安装这个包
locktt --registry=https://registry.npmjs.org/ // 替换lock中的源为指定源

接下来,我们再看一下这个locktt的代码结构

scripts/treat-lock-files.js // 处理命令行参数
lib/treat-locks/common.lock.js // 用于替换的方法(正则)
lib/treat-locks/yarn.lock.js // 针对yarn的修复方法
lib/treat-locks/npm.lock.js // 针对npm的修复方法

其中run函数就是功能主入口

// scripts/treat-lock-files.js 
function run() {
  console.log('Treating fields...');
  const folderPath = argv.folder === undefined ? '.' : argv.folder;
  const outputFolderPath = argv.outputFolder === undefined ? folderPath : argv.outputFolder;
  const npmOptions = new NpmOptions(
    argv.registry || getRegistryFromNpmCommand(),
    argv.replacePackageLockRegistry,
    argv.skipIntegrity,
  );
  npmLock(folderPath, outputFolderPath, npmOptions); // 修复npm.lock
  yarnLock(folderPath, outputFolderPath, npmOptions);// 修复yarn.lock
}
// lib/treat-locks/yarn.lock.js
const FILE_NAME = 'yarn.lock';
const fs = require('fs');
const fileUtil = require('../utils/file.util');
const commonLock = require('./common.lock');
const NpmOptions = require('./npm.options');

function treatLine(line, npmOptions) {
  if (line.startsWith('  resolved "http')) {
    // 调用通用替换方法
    return commonLock.replaceHost(line, npmOptions.registry).concat('\n');
  }
  if (line.startsWith('  integrity ') && npmOptions.skipIntegrity === false) {
    return '';
  }
  return line.concat('\n');
}

/**
 * In the yarn case the host from 'resolved' should be replaced by <INTERNAL_REGISTRY> https://github.com/yarnpkg/yarn/issues/5892#issuecomment-414796103
 * @param {string} folderPath the path where the lock file is
 * @param {string} outputFolder the path where the lock should be saved
 */
async function run(folderPath, outputFolder, npmOptions = new NpmOptions()) {
  fileUtil.checkOutputFolderExistance(outputFolder);
  const filePath = `${folderPath}/${FILE_NAME}`;
  const output = `${outputFolder}/${FILE_NAME}`;

  if (fs.existsSync(filePath, 'utf8')) {
    if (npmOptions.registry === undefined) {
      console.warn(`${filePath} file exist but the registry has not been set. Please use --registry=<NPM_REGISTRY>`);
      return false;
    }
    let content = ''; // 新文件buffer
    await fileUtil.readLineSync(filePath, async (line) => {
      // 逐行扫描旧文件,按规则替换
      content = content.concat(treatLine(line, npmOptions));
    });
    fs.writeFileSync(`${output}`, content); // buffer写入磁盘
    console.info(`${filePath} treated`);
    return true;
  }
  console.warn(`${filePath} does not exist`);
  return false;
}

module.exports = run;

总结

本文从源失效出发,分析了lock文件记录源的方式,以及我们如何进行修复,并且顺带分析了一下这个工具的运行原理。