Skip to content

Commit

Permalink
feat(pkg): add support to empty bracket syntax
Browse files Browse the repository at this point in the history
Adds ability to using empty bracket syntax as a shortcut to appending
items to the end of an array when using `npm pkg set`, e.g:

npm pkg set keywords[]=foo

It also adds support to negative indexes, so it's now also possible to
retrieve the last item of an array without knowing an exact index, e.g:

npm pkg get keywords[-1]

Relates to: npm/rfcs#402
  • Loading branch information
ruyadorno committed Jul 12, 2021
1 parent efc4313 commit 6ccc027
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 0 deletions.
7 changes: 7 additions & 0 deletions docs/content/commands/npm-pkg.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ Returned values are always in **json** format.
npm pkg set contributors[0].name='Foo' contributors[0].email='foo@bar.ca'
```
You may also append items to the end of an array using the special
empty bracket notation:
```bash
npm pkg get contributors[].name='Bar' contributors[].email='bar@bar.ca'
```
It's also possible to parse values as json prior to saving them to your
`package.json` file, for example in order to set a `"private": true`
property:
Expand Down
10 changes: 10 additions & 0 deletions lib/utils/queryable.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const cleanLeadingDot = str =>
const parseKeys = (key) => {
const sqBracketItems = new Set()
const parseSqBrackets = (str) => {
str = str.replace('[]', '[-0]')
const index = sqBracketsMatcher(str)

// once we find square brackets, we recursively parse all these
Expand Down Expand Up @@ -124,8 +125,17 @@ const setter = ({ data, key, value, force }) => {
const maybeIndex = Number(_key)
if (!Number.isNaN(maybeIndex)) {
_key = maybeIndex

// creates new array in case it's missing
if (!Object.keys(_data).length)
_data = []

// in case it's using a negative index, than calculates the
// length of the array minus that negative index, it's also used
// as a handy shortcut to empty-bracket-appending syntax since
// it converts missing indexes like this: arr[] -> arr[-0]
if (_key < 0 || Object.is(_key, -0))
_key = (_data.length) + _key
}

// retrieves the next data object to recursively iterate on,
Expand Down
32 changes: 32 additions & 0 deletions test/lib/pkg.js
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,38 @@ t.test('set single field', t => {
})
})

t.test('push to array syntax', t => {
const json = {
name: 'foo',
version: '1.1.1',
keywords: [
'foo',
],
}
npm.localPrefix = t.testdir({
'package.json': JSON.stringify(json),
})

pkg.exec(['set', 'keywords[]=bar', 'keywords[]=baz'], err => {
if (err)
throw err

t.strictSame(
readPackageJson(),
{
...json,
keywords: [
'foo',
'bar',
'baz',
],
},
'should append to arrays using empty bracket syntax'
)
t.end()
})
})

t.test('set multiple fields', t => {
const json = {
name: 'foo',
Expand Down
55 changes: 55 additions & 0 deletions test/lib/utils/queryable.js
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,61 @@ t.test('set arrays', async t => {
{ code: 'EOVERRIDEVALUE' },
'should throw an override error'
)

qqq.set('arr[]', 'c')
t.strictSame(
qqq.toJSON(),
{
arr: [
'a',
'b',
'c',
],
},
'should be able to append to array using empty bracket notation'
)

qqq.set('arr[-2]', 'B')
t.strictSame(
qqq.toJSON(),
{
arr: [
'a',
'B',
'c',
],
},
'should be able to use negative indexes'
)

qqq.set('arr[-1]', 'C')
t.strictSame(
qqq.toJSON(),
{
arr: [
'a',
'B',
'C',
],
},
'should be able to use negative indexes'
)

qqq.set('arr[].foo', 'foo')
t.strictSame(
qqq.toJSON(),
{
arr: [
'a',
'B',
'C',
{
foo: 'foo',
},
],
},
'should be able to append objects to array using empty bracket notation'
)
})

t.test('delete values', async t => {
Expand Down

0 comments on commit 6ccc027

Please sign in to comment.