0

I'm trying to write a script that can add the package.json dependencies from one project to another one programmatically.

This is probably trivial jq, although I did search around and couldn't find anything that does this exactly.

What I ultimately want to do is this:

yarn add $(get-my-deps.sh)

And so I'm writing get-my-deps right now.

  • I know the path to my package.json file
  • I know that yarn add package-name@^1.23.45 package-name-2@^2.3.4 does what I want

I figure I want to do something like:

jq ".dependencies | keys" package.json
//                  ^ this is where I'm stuck

I don't want just a list of package names, I want to convert the hash of package-name/version-range data into a string with their names and ranges, like this:

babel@^7.3.12 react@^16.0.0 react-dom@^16.0.0 webpack@^4.0.0

I think I just don't understand how the... caret works in jq. I imagine it's the conceptual equivalent of regex backreferencing.

Here's the JS code:

module.exports = () => {

const package = require('./package.json')
// or, if you prefer
const FS = require('fs')
const package = FS.readFileSync('./package.json', 'utf8')

return Object.keys(package.dependencies)
// convert to array of strings: "{name}@{range}"
.reduce((modules, modName) => modules.concat(`${modName}@${package.dependencies[modName]}`), [])
// convert to single string for bash one-liner
.join(' ')

}

I'm sure this is a pretty trivial task for anyone who is fluent with jq, so I'm going to wait a couple days so that folks can take time to put together a good explanation.

Thanks for helping me get my head wrapped around jq.


Here's an example input:

{
    "name": "chalk",
    "version": "3.0.0",
    "description": "Terminal string styling done right",
    "license": "MIT",
    "repository": "chalk/chalk",
    "main": "source",
    "engines": {
        "node": ">=8"
    },
    "scripts": {
        "test": "xo && nyc ava && tsd",
        "bench": "matcha benchmark.js"
    },
    "dependencies": {
        "ansi-styles": "^4.1.0",
        "supports-color": "^7.1.0"
    },
    "devDependencies": {
        "ava": "^2.4.0",
        "coveralls": "^3.0.7",
        "execa": "^3.2.0",
        "import-fresh": "^3.1.0",
        "matcha": "^0.7.0",
        "nyc": "^14.1.1",
        "resolve-from": "^5.0.0",
        "tsd": "^0.7.4",
        "xo": "^0.25.3"
    }
}

What must be produced:

ansi-styles@^4.1.0 supports-color@^7.1.0
peak
  • 105,803
  • 17
  • 152
  • 177
Tom
  • 8,509
  • 7
  • 49
  • 78
  • 1
    Apologies. I assumed everyone knows what a typical package.json looks like. I've posted an abridged version of chalk's file. – Tom Nov 25 '19 at 01:26

2 Answers2

2

Well, I came up with this. It works. But if this isn't the idiomatic way to do it, I'd welcome an improved version.

jq ".dependencies | to_entries | map_values( .key + \"@\" + .value ) | join(\" \")" package.json
//> "ansi-styles@^4.1.0 supports-color@^7.1.0"

POC repl

Tom
  • 8,509
  • 7
  • 49
  • 78
2

You were close; all you need was to realize how to store keys in a variable, so that you can combine each key with its corresponding value to form a single string.

Here's how you do it:

.dependencies | [ keys[] as $k | "\($k)@\(.[$k])" ] | join(" ")

Online demo at jqplay.org

keys[] expands to a stream of keys in dependencies; with as $k, we're telling jq to store each member of this stream in a variable called $k and make it available on the right hand side of the pipe. "\($k)@\(.[$k])" simply forms a string by combining $k, @ and .[$k] (i.e: the value corresponding to $k). By wrapping this expression between brackets, we're storing each value it yields in an array, and by piping this array to join(" "), we're joining the array elements by a space to form a single string. That's all.

oguz ismail
  • 1
  • 16
  • 47
  • 69