From 863c199ed42a04032ba28a0a491c7cf582586d5a Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Thu, 19 Oct 2023 08:37:14 -0500 Subject: [PATCH 1/9] fix!: codename should be null on non-lts (#21) --- index.js | 2 +- test/index.js | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 8d890d0..bb1a7e7 100644 --- a/index.js +++ b/index.js @@ -89,7 +89,7 @@ async function getLatestVersionsByCodename ({ now, cache, mirror, ignoreFutureRe const aliases = versions.reduce((obj, ver) => { const { major, minor, patch, tag } = splitVersion(ver.version) const versionName = major !== '0' ? `v${major}` : `v${major}.${minor}` - const codename = ver.lts ? ver.lts.toLowerCase() : versionName + const codename = ver.lts ? ver.lts.toLowerCase() : null const version = tag !== '' ? `${major}.${minor}.${patch}-${tag}` : `${major}.${minor}.${patch}` const s = schedule[versionName] diff --git a/test/index.js b/test/index.js index 9b95d8c..5780728 100644 --- a/test/index.js +++ b/test/index.js @@ -204,4 +204,9 @@ suite('nv', () => { assert.strictEqual(versions[0].minor, 16) assert.strictEqual(versions[0].patch, 1) }) + + test('codename null on non-lts', async () => { + const versions = await nv('0.8.0', { now }) + assert.strictEqual(versions[0].codename, null) + }) }) From 2aefa09cd7dc41d792e3a48d1a51ac7b7d6d93aa Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Thu, 19 Oct 2023 08:56:06 -0500 Subject: [PATCH 2/9] feat!: tag => prerelease and use semver (#20) --- index.d.ts | 2 +- index.js | 10 ++-------- index.test-d.ts | 2 +- test/index.js | 2 +- 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/index.d.ts b/index.d.ts index 1f7daa9..58b9774 100644 --- a/index.d.ts +++ b/index.d.ts @@ -11,7 +11,7 @@ interface VersionInfo { major: number; minor: number; patch: number; - tag: string; + prerelease?: string[]; codename: string; versionName: string; start?: Date; diff --git a/index.js b/index.js index bb1a7e7..5635882 100644 --- a/index.js +++ b/index.js @@ -87,10 +87,9 @@ async function getLatestVersionsByCodename ({ now, cache, mirror, ignoreFutureRe const lts = {} const aliases = versions.reduce((obj, ver) => { - const { major, minor, patch, tag } = splitVersion(ver.version) + const { version, major, minor, patch, prerelease } = semver.parse(ver.version) const versionName = major !== '0' ? `v${major}` : `v${major}.${minor}` const codename = ver.lts ? ver.lts.toLowerCase() : null - const version = tag !== '' ? `${major}.${minor}.${patch}-${tag}` : `${major}.${minor}.${patch}` const s = schedule[versionName] // Version Object @@ -99,7 +98,7 @@ async function getLatestVersionsByCodename ({ now, cache, mirror, ignoreFutureRe major, minor, patch, - tag, + prerelease, codename, versionName, start: s && s.start && new Date(s.start), @@ -215,8 +214,3 @@ async function getLatestVersionsByCodename ({ now, cache, mirror, ignoreFutureRe return aliases } - -function splitVersion (ver) { - const [, major, minor, patch, tag] = /^v([0-9]*)\.([0-9]*)\.([0-9]*)(?:-([0-9A-Za-z-_]+))?/.exec(ver).map((n, i) => i < 4 ? parseInt(n, 10) : n || '') - return { major, minor, patch, tag } -} diff --git a/index.test-d.ts b/index.test-d.ts index a8ce2ae..4458798 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -7,7 +7,7 @@ import assert from 'node:assert' assert(versions[0].major) assert(versions[0].minor) assert(versions[0].patch) - assert(versions[0].tag) + assert(!versions[0].prerelease) assert(versions[0].codename) assert(versions[0].versionName) assert(versions[0].start) diff --git a/test/index.js b/test/index.js index 5780728..6b7648e 100644 --- a/test/index.js +++ b/test/index.js @@ -165,7 +165,7 @@ suite('nv', () => { assert.strictEqual(versions[0].major, 13) assert.strictEqual(versions[0].minor, 0) assert.strictEqual(versions[0].patch, 0) - assert.strictEqual(versions[0].tag, 'v8-canary20191022e5d3472f57') + assert.deepStrictEqual(versions[0].prerelease, ['v8-canary20191022e5d3472f57']) assert.strictEqual(versions[0].versionName, 'v13') }) From 1eb20cbae1c9fe31e2b9b50802b3e8825ee248d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominykas=20Bly=C5=BE=C4=97?= Date: Tue, 16 Jul 2024 20:31:39 +0300 Subject: [PATCH 3/9] feat: update engines to >=18 and use pkgjs/action (#22) * ci: use pkgjs/action * feat!: update engines --------- Co-authored-by: Wes Todd --- .github/workflows/test.yml | 15 +-------------- package.json | 3 +++ 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2c1f025..60de7b9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,17 +22,4 @@ jobs: - name: npm lint run: npm run lint test: - runs-on: ubuntu-latest - strategy: - matrix: - node-version: [10.x, 12.x, 13.x, 14.x, 16.x, 18.x, 20.x, 22.x, 24.x] - steps: - - uses: actions/checkout@v5 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v5 - with: - node-version: ${{ matrix.node-version }} - - name: npm install - run: npm install - - name: npm test - run: npm run test + uses: pkgjs/action/.github/workflows/node-test.yaml@v0 diff --git a/package.json b/package.json index 396744d..9c8d117 100644 --- a/package.json +++ b/package.json @@ -49,5 +49,8 @@ }, "bin": { "nv": "./bin/nv" + }, + "engines": { + "node": "^18 || ^20 || ^22" } } From 84c7735f918ca1c91cb384f7bc2ffd3a642d80ce Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Tue, 16 Jul 2024 10:37:10 -0700 Subject: [PATCH 4/9] fix: mocha@10.6.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9c8d117..49fd77b 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ }, "devDependencies": { "gen-esm-wrapper": "^1.1.0", - "mocha": "^9.2.2", + "mocha": "^10.6.0", "standard": "^17.1.2", "standard-version": "^9.3.2", "tsd": "^0.27.0" From 6c87fbe96ccf50b0b92d586e36d43e7b5e5869ab Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Tue, 16 Jul 2024 10:38:46 -0700 Subject: [PATCH 5/9] fix: tsd@0.31.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 49fd77b..f2538a8 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "mocha": "^10.6.0", "standard": "^17.1.2", "standard-version": "^9.3.2", - "tsd": "^0.27.0" + "tsd": "^0.31.1" }, "bin": { "nv": "./bin/nv" From 4830222487d72af703671f2689562f4224f95f34 Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Tue, 16 Jul 2024 10:39:13 -0700 Subject: [PATCH 6/9] fix: gen-esm-wrapper@1.1.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f2538a8..9105794 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "yargs": "^16.2.0" }, "devDependencies": { - "gen-esm-wrapper": "^1.1.0", + "gen-esm-wrapper": "^1.1.3", "mocha": "^10.6.0", "standard": "^17.1.2", "standard-version": "^9.3.2", From 302ff80bd74cf64ba2da18d9ba88c0bfebed0cf4 Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Tue, 16 Jul 2024 10:39:42 -0700 Subject: [PATCH 7/9] fix: standard-version@9.5.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9105794..894e6d1 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "gen-esm-wrapper": "^1.1.3", "mocha": "^10.6.0", "standard": "^17.1.2", - "standard-version": "^9.3.2", + "standard-version": "^9.5.0", "tsd": "^0.31.1" }, "bin": { From 98fb2a977fa1d6c16df36f50c9e0b150724303cd Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 16 Jul 2024 13:15:52 -0700 Subject: [PATCH 8/9] feat: add `--only-version` option to skip JSON and only print version number --- bin/nv | 8 +++++++- test/cli.js | 15 ++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/bin/nv b/bin/nv index 18fc9a9..90477f9 100755 --- a/bin/nv +++ b/bin/nv @@ -23,6 +23,10 @@ require('yargs') default: false, description: 'Only show latest version in each semver major range' }) + yargs.option('only-version', { + type: 'boolean', + description: 'show version number only' + }) }, handler: (argv) => { nv(argv.versions, { @@ -31,7 +35,9 @@ require('yargs') }) .then(result => { result.forEach(r => { - if (argv.prettyJson === false) { + if (argv['only-version']) { + console.log(r.version) + } else if (argv.prettyJson === false) { console.log(JSON.stringify(r)) } else if (!isNaN(parseInt(argv.prettyJson, 10))) { console.log(JSON.stringify(r, null, parseInt(argv.prettyJson, 10))) diff --git a/test/cli.js b/test/cli.js index e61d7c8..0b653c8 100644 --- a/test/cli.js +++ b/test/cli.js @@ -3,6 +3,7 @@ const assert = require('assert') const { suite, test } = require('mocha') const { execFileSync } = require('child_process') const path = require('path') +const semver = require('semver') const nv = path.join(__dirname, '..', 'bin', 'nv') const cwd = path.join(__dirname, '..') @@ -17,6 +18,7 @@ suite('nv cli', () => { const result = JSON.parse(execFileSync(nv, ['ls', '8'], { cwd }).toString()) assert.strictEqual(result.codename, 'carbon') }) + test('should contain output newline json', () => { const result = execFileSync(nv, ['ls', '8.x', '--no-pretty-json'], { cwd }) .toString().trim().split('\n') @@ -24,7 +26,18 @@ suite('nv cli', () => { assert(Array.isArray(result)) result.forEach((r) => { - assert(r.version.startsWith('8.')) + assert(semver.satisfies(r.version, '8.x')) + }) + }) + + test('only outputs the version number', () => { + const result = execFileSync(nv, ['ls', '8.x', '--only-version'], { cwd: cwd }) + .toString().trim().split('\n') + + assert(Array.isArray(result)) + result.forEach((r) => { + assert(semver.satisfies(r, '8.x')) + assert(semver.valid(r)) }) }) test('should only contain the latest of each major', () => { From b6bc159f96c63c51b3e44707dde2f66131a210df Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 16 Jul 2024 14:01:49 -0700 Subject: [PATCH 9/9] feat: add `engines` and `cwd` options --- README.md | 103 +++++++++++++++++++++++++++++ bin/nv | 7 +- index.js | 17 +++++ test/cli.js | 59 +++++++++++++++++ test/fixtures/engines/package.json | 5 ++ test/index.js | 56 ++++++++++++++++ 6 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/engines/package.json diff --git a/README.md b/README.md index ecb5442..2409032 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,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** diff --git a/bin/nv b/bin/nv index 90477f9..7f4b7a8 100755 --- a/bin/nv +++ b/bin/nv @@ -27,9 +27,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, latestOfMajorOnly: argv.latestOfMajorOnly }) @@ -47,7 +52,7 @@ require('yargs') }) }).catch(e => { console.error(e) - process.exitCode = e.code || 1 + process.exitCode ||= e.code || 1 }) } }) diff --git a/index.js b/index.js index 5635882..269fa43 100644 --- a/index.js +++ b/index.js @@ -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() @@ -9,6 +12,8 @@ module.exports = async function (alias = 'lts_active', opts = {}) { const mirror = opts.mirror || 'https://nodejs.org/dist/' const latestOfMajorOnly = opts.latestOfMajorOnly || false const ignoreFutureReleases = opts.ignoreFutureReleases || false + const engines = opts.engines + const cwd = opts.cwd || process.cwd() const a = Array.isArray(alias) ? alias : [alias] const versions = await getLatestVersionsByCodename({ @@ -31,6 +36,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) => { diff --git a/test/cli.js b/test/cli.js index 0b653c8..57151a2 100644 --- a/test/cli.js +++ b/test/cli.js @@ -40,6 +40,7 @@ suite('nv cli', () => { assert(semver.valid(r)) }) }) + test('should only contain the latest of each major', () => { const result = execFileSync(nv, ['ls', '16.x || 18.x', '--no-pretty-json', '--latest-of-major-only'], { cwd }) .toString().trim().split('\n') @@ -50,4 +51,62 @@ suite('nv cli', () => { assert(result[0].version.startsWith('16.')) assert(result[1].version.startsWith('18.')) }) + + 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' + ]) + }) }) diff --git a/test/fixtures/engines/package.json b/test/fixtures/engines/package.json new file mode 100644 index 0000000..0a6e7f7 --- /dev/null +++ b/test/fixtures/engines/package.json @@ -0,0 +1,5 @@ +{ + "engines": { + "node": "^8.10" + } +} diff --git a/test/index.js b/test/index.js index 6b7648e..3b4d829 100644 --- a/test/index.js +++ b/test/index.js @@ -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 @@ -209,4 +210,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' + ]) + }) })