Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add engines and cwd options #37

Merged
merged 1 commit into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,109 @@ console.log(versions.map((v) => v.version))
*/
```

## Command line interface (CLI)

Options:
- `--only-version`: instead of the entire output, only print one version number per line
- `--engines`: only print versions that match the current directory's `engines.node` field
- `--engines=lts`: only print LTS versions that also match the current directory's `engines.node` field

```sh
$ nv ls lts
{
"version": "18.20.4",
"major": 18,
"minor": 20,
"patch": 4,
"tag": "",
"codename": "hydrogen",
"versionName": "v18",
"start": "2022-04-19T00:00:00.000Z",
"lts": "2022-10-25T00:00:00.000Z",
"maintenance": "2023-10-18T00:00:00.000Z",
"end": "2025-04-30T00:00:00.000Z",
"releaseDate": "2024-07-08T00:00:00.000Z",
"isLts": true,
"files": [
"aix-ppc64",
"headers",
"linux-arm64",
"linux-armv7l",
"linux-ppc64le",
"linux-s390x",
"linux-x64",
"osx-arm64-tar",
"osx-x64-pkg",
"osx-x64-tar",
"src",
"win-x64-7z",
"win-x64-exe",
"win-x64-msi",
"win-x64-zip",
"win-x86-7z",
"win-x86-exe",
"win-x86-msi",
"win-x86-zip"
],
"dependencies": {
"npm": "10.7.0",
"v8": "10.2.154.26",
"uv": "1.44.2",
"zlib": "1.3.0.1-motley",
"openssl": "3.0.13+quic"
}
}
{
"version": "20.15.1",
"major": 20,
"minor": 15,
"patch": 1,
"tag": "",
"codename": "iron",
"versionName": "v20",
"start": "2023-04-18T00:00:00.000Z",
"lts": "2023-10-24T00:00:00.000Z",
"maintenance": "2024-10-22T00:00:00.000Z",
"end": "2026-04-30T00:00:00.000Z",
"releaseDate": "2024-07-08T00:00:00.000Z",
"isLts": true,
"files": [
"aix-ppc64",
"headers",
"linux-arm64",
"linux-armv7l",
"linux-ppc64le",
"linux-s390x",
"linux-x64",
"osx-arm64-tar",
"osx-x64-pkg",
"osx-x64-tar",
"src",
"win-arm64-7z",
"win-arm64-zip",
"win-x64-7z",
"win-x64-exe",
"win-x64-msi",
"win-x64-zip",
"win-x86-7z",
"win-x86-exe",
"win-x86-msi",
"win-x86-zip"
],
"dependencies": {
"npm": "10.7.0",
"v8": "11.3.244.8",
"uv": "1.46.0",
"zlib": "1.3.0.1-motley",
"openssl": "3.0.13+quic"
}
}

$ nv ls lts --only-version
18.20.4
20.15.1
```

## Supported Aliases

**Support Aliases**
Expand Down
7 changes: 6 additions & 1 deletion bin/nv
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,14 @@ require('yargs')
type: 'boolean',
description: 'show version number only'
})
yargs.option('engines', {
type: 'string',
description: 'read the value of `engines.node`. if a value is provided, and it satisfies, the version is shown; if not, the max satisfying version is shown'
})
},
handler: (argv) => {
nv(argv.versions, {
engines: argv.engines,
mirror: argv.mirror
})
.then(result => {
Expand All @@ -41,7 +46,7 @@ require('yargs')
})
}).catch(e => {
console.error(e)
process.exitCode = e.code || 1
process.exitCode ||= e.code || 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, while I think I agree with this change it actually makes me wonder if this should instead handle process.exit itself?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it could, sure - but this way it will still exit the same, except that it’ll preserve a preexisting nonzero exit code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, that's why I was on board with the change. It just got me thinking about the other thing. If something sets and exit code but doesn't exit I always feel like that is an "action at a distance" which I dont like.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i 100% agree but long ago node shifted into "set the exit code" as idiomatic instead of "exit with a code", and i've given up fighting it :-)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, interesting. I would love to read more about that shift. Anyway, I didn't want to block on this either way. Seems like this is good to merge?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Go for it!

})
}
})
Expand Down
17 changes: 17 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
'use strict'
const { readFile } = require('fs/promises')
const { join } = require('path')

const got = require('got')
const semver = require('semver')
const _cache = new Map()
Expand All @@ -8,6 +11,8 @@ module.exports = async function (alias = 'lts_active', opts = {}) {
const cache = opts.cache || _cache
const mirror = opts.mirror || 'https://nodejs.org/dist/'
const latestOfMajorOnly = opts.latestOfMajorOnly || false
const engines = opts.engines
const cwd = opts.cwd || process.cwd()

const a = Array.isArray(alias) ? alias : [alias]
const versions = await getLatestVersionsByCodename(now, cache, mirror)
Expand All @@ -25,6 +30,18 @@ module.exports = async function (alias = 'lts_active', opts = {}) {
return m
}, {})

if (typeof engines === 'string' || engines === true) {
const { engines: { node } = { node: '*' } } = JSON.parse(await readFile(join(cwd, 'package.json'), 'utf8'))

m = Object.fromEntries(Object.entries(m).filter(([version]) => semver.satisfies(version, node)))

const matching = Object.entries(m).filter(([version]) => semver.satisfies(version, engines))

if (matching.length > 0) {
m = Object.fromEntries(matching)
}
}

// If only latest major is true, filter out all but latest
if (latestOfMajorOnly) {
const vers = Object.values(m).reduce((latestMajor, v) => {
Expand Down
58 changes: 58 additions & 0 deletions test/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,62 @@ suite('nv cli', () => {
assert(semver.valid(r))
})
})

test('works with `--engines`', () => {
const result = execFileSync(nv, ['ls', '8.x', '--only-version', '--engines=">=8"'], { cwd: path.join(__dirname, 'fixtures', 'engines') })
.toString().trim().split('\n')

assert.deepEqual(result, [
'8.10.0',
'8.11.0',
'8.11.1',
'8.11.2',
'8.11.3',
'8.11.4',
'8.12.0',
'8.13.0',
'8.14.0',
'8.14.1',
'8.15.0',
'8.15.1',
'8.16.0',
'8.16.1',
'8.16.2',
'8.17.0'
])

const result2 = execFileSync(nv, ['ls', '8.x', '--only-version', '--engines=">=8.15"'], { cwd: path.join(__dirname, 'fixtures', 'engines') })
.toString().trim().split('\n')

assert.deepEqual(result2, [
'8.15.0',
'8.15.1',
'8.16.0',
'8.16.1',
'8.16.2',
'8.17.0'
])

const result3 = execFileSync(nv, ['ls', '8.x', '--only-version', '--engines'], { cwd: path.join(__dirname, 'fixtures', 'engines') })
.toString().trim().split('\n')

assert.deepEqual(result3, [
'8.10.0',
'8.11.0',
'8.11.1',
'8.11.2',
'8.11.3',
'8.11.4',
'8.12.0',
'8.13.0',
'8.14.0',
'8.14.1',
'8.15.0',
'8.15.1',
'8.16.0',
'8.16.1',
'8.16.2',
'8.17.0'
])
})
})
5 changes: 5 additions & 0 deletions test/fixtures/engines/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"engines": {
"node": "^8.10"
}
}
56 changes: 56 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict'
const { suite, test } = require('mocha')
const assert = require('assert')
const path = require('path')
const nv = require('..')

// 2019-02-07T16:15:49.683Z
Expand Down Expand Up @@ -182,4 +183,59 @@ suite('nv', () => {
const versions = await nv('0.8.0', { now })
assert.strictEqual(versions[0].codename, null)
})

test('engines option', async () => {
const versions = await nv('8.x', { now, cwd: path.join(__dirname, 'fixtures', 'engines'), engines: '>=8' })

assert.deepEqual(versions.map(x => x.version), [
'8.10.0',
'8.11.0',
'8.11.1',
'8.11.2',
'8.11.3',
'8.11.4',
'8.12.0',
'8.13.0',
'8.14.0',
'8.14.1',
'8.15.0',
'8.15.1',
'8.16.0',
'8.16.1',
'8.16.2',
'8.17.0'
])

const versions2 = await nv('8.x', { now, cwd: path.join(__dirname, 'fixtures', 'engines'), engines: '>=8.15' })

assert.deepEqual(versions2.map(x => x.version), [
'8.15.0',
'8.15.1',
'8.16.0',
'8.16.1',
'8.16.2',
'8.17.0'
])

const versions3 = await nv('8.x', { now, cwd: path.join(__dirname, 'fixtures', 'engines'), engines: true })

assert.deepEqual(versions3.map(x => x.version), [
'8.10.0',
'8.11.0',
'8.11.1',
'8.11.2',
'8.11.3',
'8.11.4',
'8.12.0',
'8.13.0',
'8.14.0',
'8.14.1',
'8.15.0',
'8.15.1',
'8.16.0',
'8.16.1',
'8.16.2',
'8.17.0'
])
})
})