Skip to content

Commit ce7abe9

Browse files
committed
feat(exe): support latest and latest-lts for nodeVersion
1 parent 944e92a commit ce7abe9

File tree

2 files changed

+41
-7
lines changed

2 files changed

+41
-7
lines changed

packages/exe/src/download.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
getArchiveExtension,
1010
getBinaryPathInArchive,
1111
getDownloadUrl,
12-
normalizeNodeVersion,
12+
resolveNodeVersion,
1313
type ExeTarget,
1414
} from './platform.ts'
1515

@@ -24,7 +24,7 @@ export async function resolveNodeBinary(
2424
logger?: MinimalLogger,
2525
): Promise<string> {
2626
debug('Resolving Node.js binary for target: %O', target)
27-
target.nodeVersion = normalizeNodeVersion(target)
27+
target.nodeVersion = await resolveNodeVersion(target.nodeVersion)
2828

2929
const cachedPath = getCachedBinaryPath(target)
3030
debug('Cache path: %s', cachedPath)

packages/exe/src/platform.ts

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import semver from 'semver'
21
import satisfies from 'semver/functions/satisfies.js'
2+
import valid from 'semver/functions/valid.js'
33

44
export type ExePlatform = 'win' | 'darwin' | 'linux'
55
export type ExeArch = 'x64' | 'arm64'
@@ -8,7 +8,12 @@ export interface ExeTarget {
88
platform: ExePlatform
99
arch: ExeArch
1010
/**
11-
* Node.js version to use for the executable. Should be a valid Node.js version string (e.g., "25.7.0").
11+
* Node.js version to use for the executable.
12+
*
13+
* Accepts a valid semver string (e.g., `"25.7.0"`), or the special values
14+
* `"latest"` / `"latest-lts"` which resolve the version automatically from
15+
* {@link https://nodejs.org/dist/index.json}.
16+
*
1217
* The minimum required version is 25.7.0, which is when SEA support was added to Node.js.
1318
*/
1419
nodeVersion: string
@@ -53,11 +58,40 @@ export function getBinaryPathInArchive(target: ExeTarget): string {
5358
return `${dirName}/bin/node`
5459
}
5560

56-
export function normalizeNodeVersion(target: ExeTarget): string {
57-
const version = semver.valid(target.nodeVersion)
61+
interface NodeRelease {
62+
version: string
63+
lts: string | false
64+
}
65+
66+
const NODE_DIST_INDEX_URL = 'https://nodejs.org/dist/index.json'
67+
68+
export async function resolveNodeVersion(nodeVersion: string): Promise<string> {
69+
if (nodeVersion === 'latest' || nodeVersion === 'latest-lts') {
70+
const response = await fetch(NODE_DIST_INDEX_URL)
71+
if (!response.ok) {
72+
throw new Error(
73+
`Failed to fetch Node.js releases: HTTP ${response.status} from ${NODE_DIST_INDEX_URL}`,
74+
)
75+
}
76+
77+
const releases = (await response.json()) as NodeRelease[]
78+
79+
const release =
80+
nodeVersion === 'latest'
81+
? releases[0]
82+
: releases.find((r) => r.lts !== false)
83+
84+
if (!release) {
85+
throw new Error(`No matching Node.js release found for "${nodeVersion}".`)
86+
}
87+
88+
nodeVersion = release.version.replace(/^v/, '')
89+
}
90+
91+
const version = valid(nodeVersion)
5892
if (!version) {
5993
throw new Error(
60-
`Invalid Node.js version: ${target.nodeVersion}. ` +
94+
`Invalid Node.js version: ${nodeVersion}. ` +
6195
`Please provide a valid version string (e.g., "25.7.0").`,
6296
)
6397
}

0 commit comments

Comments
 (0)