How to resolve the problem that NPM packages require mandatory installation of the lowest version of their dependencies?

Questions briefly

Recently, the company was developing a small application cross-end framework and encountered a small problem — the NPM package required the mandatory installation of the lowest version of its dependencies. In a nutshell, it looks like this:

  • engineeringnode_moduleInstall a package that is responsible for the compilation process namedcompile
  • engineeringnode_moduleThere is also a package for the component library namedcomponents

Now the package version of Compile NPM is upgraded to 0.2.0, we need the package version of Components to upgrade to 1.3.0. The reason may be that the package compile NPM has changed a certain compilation rule, and a certain way of writing in the component library needs to be changed, otherwise an error will be reported.

The problem is that the company has multiple business lines and multiple projects. It is impossible for me to upgrade the components package of each project every time the compile package is upgraded, otherwise I will be killed by my business classmates.

It’s actually quite common…

Vue also encountered this problem. There is such a sentence on vUE official website, as follows:

When a new version of each VUE package is released, a corresponding version of vue-template-Compiler is also released. The version of the compiler must be synchronized with the base VUE package so that vue-Loader generates run-time compatible code. This means that every time you upgrade the VUE package in your project, you should also upgrade the VUe-template-Compiler.

So, essentially, the question is, how do you guarantee a mandatory minimum version requirement between dependent packages

The solution

Solution 1: Remotely distribute the Config configuration

In our project, every time the developers start the project, there will be a small egg, we will be friendly and warm reminder of the developers, what the weather is like today. Essentially, at startup, pull a dynamic script from the server to execute.

We thought of something we could do in this section. For example, check that the version the user installed is ok.

On the server side, we maintain a configuration file that describes the mandatory minimum version requirements between dependent packages.

The server can build its own Node service, or even, at its simplest, put it in a Git repository.

Every time the developer executes NPM run dev, which is the pre hook, it goes to the server and pulls the configuration file, and then checks to see if the user’s locally installed version matches the description in the configuration file.

One problem with such schemes, however, is that they are not easy to maintain.

First, every time I release a new Compile package, the version is updated, so the configuration file needs to be updated as well.

Second, the configuration file looks like this:

{
  "Compile compile package": {
    "0.0.1 version": {
      "A dependent package": "0.1.2"."B dependent package": "0.1.3"."C dependency package": "0.1.3",},"Hundreds version": {
      "A dependent package": "0.1.2"."B dependent package": "0.1.4"."C dependency package": "0.1.5",}}}Copy the code

This configuration file is public. Each time the compile package is upgraded, a new piece of code is added, and over time, this configuration will become very large.

So, in the end, we chose the second option.

Scheme 2: hard code

In fact, frameworks like Vue do the same.

Write the logic of enforcing dependent version verification directly in code.

Before initialization, verify that its own version, and dependent versions, match the description in the configuration file. The configuration file is in the package, along with the version.

The specific implementation

Here is a concrete implementation of the code:

The compare method is defined to compare the size of the version number represented by “0.2.3” and “0.3.4”

let _isVersion = (v) = > {
    let result = /^\d+(\.\d+)*$/.test(v);
    return result;
};

let compare = (v1, v2) = > {
    if (_isVersion(v1) && _isVersion(v2)) {
        v1 = v1.toString().split('. ');
        v2 = v2.toString().split('. ');
        for (let i = 0, l1 = v1.length, l2 = v2.length; i < l1 || i < l2; i++) {
            let n1 = parseInt(v1[i], 10);
            let n2 = parseInt(v2[i], 10);
            if (n1 < n2) {
                return - 1;
            } else if (n1 > n2) {
                return 1; }}return 0;
    }
    return console.error('version value is invalid');
};
Copy the code

The configuration file config.js is defined below

// Compiler depends on the configuration of the lowest version of the package
module.exports = {
    'A dependency package ': {
        'max': ' '.'min': '0.1.2'.'require': true}}Copy the code

The _checkDependencyVersion method is defined below:

// Check the version of the dependent package
const _checkDependencyVersion = (a)= > {
    return new Promise((resolve, reject) = > {
        let depMinOption = require('./config.js');
        for (let key in depMinOption) {
            let dep = depMinOption[key];
            try {
                let depSource = require(`${key}/package.json`);
                let sourceVersion = depSource.version;
                if (dep.min && compare(sourceVersion, dep.min) === - 1) {
                    console.log(` | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |${chalk.red('ERROR! ')}
                    |    ${chalk.red(` toolkit rely on${key}The lowest version of${dep.min}, please upgrade${key}${dep.min}And above `)}| | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * `);
                    resolve(false);
                }
                if (dep.max && compare(sourceVersion, dep.max) === 1) {
                    console.log(` | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |${chalk.red('ERROR! ')}
                    |    ${chalk.red(` toolkit rely on${key}The highest version of the${dep.max}Please adjust${key}Version `)}| | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * `);
                    resolve(false);
                }
        
                resolve(true)}catch (e) {
                console.log(e)
                if (dep.require) {
                    console.log(` | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |${chalk.red('ERROR! ')}
                    |    ${chalk.red(` toolkit rely on${key} ${dep.min ? 'Lowest version:' + dep.min: ' '} ${dep.max ? 'Highest version:' + dep.max: ' '}`)}| | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * `); }}}})}Copy the code

The _checkDependencyVersion method will be executed immediately after NPM run dev, and if it is found that the local version of the developer is not ok, the developer will be reminded to upgrade the corresponding package version.

If you have a better solution, feel free to leave a comment below.