Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parsing Bower's Semantic Version Syntax

Bower seems to deviate from the semver spec in that I sometimes see dependencies that look like this (from 2klic-angular/bower.json):

  "dependencies": {
    "angulargrid": "s-yadav/angulargrid#^0.4.0"
  }

This question goes a long way towards explaining semver itself but not so much what's going on with the s-yadav/angulargrid# portion.

Looking at bower/lib/node_modules/bower-endpoint-parser/index.js

I see the following code:

function decompose(endpoint) {
    // Note that we allow spaces in targets and sources but they are trimmed
    var regExp = /^(?:([\w\-]|(?:[\w\.\-]+[\w\-])?)=)?([^\|#]+)(?:#(.*))?$/;
    var matches = endpoint.match(regExp);
    var target;
    var error;

    if (!matches) {
        error = new Error('Invalid endpoint: ' + endpoint);
        error.code = 'EINVEND';
        throw error;
    }

    target = trim(matches[3]);

    return {
        name: trim(matches[1]),
        source: trim(matches[2]),
        target: isWildcard(target) ? '*' : target
    };
}

So it seems that a repository source can be specified as part of the dependency version using # as a delimiter.

However I've not been able to find anything that describes this in the bower docs.

Are there any other caveats to be aware of with Bowers interpretation of semver or is this the only one, and is it sufficient to split the string at # to find the requirement expression?

like image 994
andrew Avatar asked Nov 08 '22 16:11

andrew


1 Answers

You can find the relevant code in bower/lib/node_modules/bower-endpoint-parser/index.js in json2decomposed:

function json2decomposed(key, value) {
    ...
    key = trim(key);
    value = trim(value);
    ...
    endpoint = key + '=';
    split = value.split('#').map(trim);

    // If # was found, the source was specified
    if (split.length > 1) {
        endpoint += (split[0] || key) + '#' + split[1];
    // Check if value looks like a source
    } else if (isSource(value)) {
        endpoint += value + '#*';
    // Otherwise use the key as the source
    } else {
        endpoint += key + '#' + split[0];
    }

    return decompose(endpoint);
}

So what later becomes the target is generated by splitting the value from the JSON dependency array by #. This target is parsed by a resolver, so the actual behaviour depends on the resolver used, but the typical resolver uses node-semver, if node-semver can parse it. Otherwise it uses commit ids, branch names, tags etc.

So splitting the string by '#' and trimming whitespace afterwards is sufficient to find the requirements expression, but it may not be a semver version after all.

like image 155
Marcel Krüger Avatar answered Nov 21 '22 00:11

Marcel Krüger