Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/brave-alpha-releases.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@node-core/website-i18n': patch
---

Add the Alpha release warning.
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ const getPage: FC<PageParams> = async props => {
if (version === 'current') {
const releaseData = await provideReleaseData();

const release = releaseData.find(release => release.status === 'Current');
// There's no Current release while the next major is in its Alpha phase
const release =
releaseData.find(release => release.status === 'Current') ??
releaseData.find(release => release.status === 'LTS');

redirect(`/${locale}/download/archive/${release?.versionWithPrefix}`);
}
Expand Down
10 changes: 9 additions & 1 deletion apps/site/components/Downloads/Release/VersionDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ const VersionDropdown: FC = () => {
({ versionWithPrefix }) => versionWithPrefix === version
);

if (release?.status === 'LTS' && pathname.includes('current')) {
if (
release?.status === 'LTS' &&
(pathname.includes('current') || pathname.includes('alpha'))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

we don't need that right ? since this for redirecting "old" link

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It's not strictly needed, it only keeps the URL consistent with the selected release, like we do for /current. Happy to drop it if you prefer

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

👍 I think this is fine to make sure the URL is what it should be

) {
redirect({ href: '/download', locale });
return;
}
Expand All @@ -37,6 +40,11 @@ const VersionDropdown: FC = () => {
return;
}

if (release?.status === 'Alpha' && !pathname.includes('alpha')) {
redirect({ href: '/download/alpha', locale });
return;
}

setVersion(version);
};

Expand Down
8 changes: 6 additions & 2 deletions apps/site/components/withDownloadSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
ReleasesProvider,
} from '#site/providers/releaseProvider';

import type { NodeRelease } from '../types';
import type { NodeRelease, NodeReleaseStatus } from '../types';
import type { FC, PropsWithChildren } from 'react';

type WithDownloadSectionProps = PropsWithChildren<{
Expand Down Expand Up @@ -39,7 +39,11 @@ const WithDownloadSection: FC<WithDownloadSectionProps> = async ({
.concat(localeSnippets);

// Decides which initial release to use based on the current pathname
const initialRelease = pathname.endsWith('/current') ? 'Current' : 'LTS';
const initialRelease: Array<NodeReleaseStatus> = pathname.endsWith('/alpha')
? ['Alpha', 'Current', 'LTS']
: pathname.endsWith('/current')
? ['Current', 'LTS']
: ['LTS'];

return (
<WithNodeRelease status={initialRelease}>
Expand Down
16 changes: 16 additions & 0 deletions apps/site/components/withReleaseAlertBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,22 @@ const WithReleaseAlertBox: FC<WithReleaseAlertBoxProps> = ({ status }) => {
})}
</AlertBox>
);
case 'Alpha':
return (
<AlertBox
title={t('components.common.alertBox.warning')}
level="warning"
size="small"
>
{t.rich('layouts.download.codeBox.alphaVersionWarning', {
link: text => (
<Link href="/blog/announcements/evolving-the-nodejs-release-schedule">
{text}
</Link>
),
})}
</AlertBox>
);
case 'LTS':
return (
<AlertBox
Expand Down
76 changes: 76 additions & 0 deletions apps/site/next-data/generators/__tests__/releaseData.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ describe('generateReleaseData', () => {
semver: { major: 20, raw: '20.12.0' },
dependencies: { npm: '10.8.2', v8: '11.3.244.8' },
lts: { isLts: false },
alpha: { isAlpha: false },
releaseDate: '2026-03-26',
modules: { version: '115' },
},
Expand Down Expand Up @@ -82,6 +83,7 @@ describe('generateReleaseData', () => {
semver: { major: 22, raw: '22.7.0' },
dependencies: { npm: '10.9.0', v8: '12.4.254.10' },
lts: { isLts: true },
alpha: { isAlpha: false },
releaseDate: '2026-02-18',
modules: { version: '124' },
},
Expand Down Expand Up @@ -110,6 +112,7 @@ describe('generateReleaseData', () => {
semver: { major: 24, raw: '24.1.0' },
dependencies: { npm: '11.1.0', v8: '13.0.12.7' },
lts: { isLts: false },
alpha: { isAlpha: false },
releaseDate: '2026-03-10',
modules: { version: '130' },
},
Expand All @@ -130,6 +133,77 @@ describe('generateReleaseData', () => {
assert.equal(result[0]?.status, 'Current');
});

it('returns Alpha when release is not EOL and latest is a pre-release', async t => {
const result = await runWithNodevuData(t, '2026-11-01', {
27: {
releases: {
'v27.0.0-alpha.0.0.0': {
semver: { major: 27, raw: '27.0.0-alpha.0.0.0' },
dependencies: { npm: '11.6.0', v8: '14.6.0.0' },
lts: { isLts: false },
alpha: { isAlpha: true },
releaseDate: '2026-10-28',
modules: { version: '150' },
},
},
support: {
phases: {
dates: {
alpha: '2026-10-28',
start: '2027-04-22',
maintenance: '2027-10-20',
end: '2030-04-30',
},
},
},
},
});

assert.equal(result[0]?.status, 'Alpha');
assert.equal(result[0]?.versionWithPrefix, 'v27.0.0-alpha.0.0.0');
});

it('returns Current when latest is a stable release following pre-releases', async t => {
const result = await runWithNodevuData(t, '2027-04-22', {
27: {
releases: {
'v27.0.0': {
semver: { major: 27, raw: '27.0.0' },
dependencies: { npm: '11.6.0', v8: '14.6.0.0' },
lts: { isLts: false },
alpha: { isAlpha: false },
releaseDate: '2027-04-22',
modules: { version: '150' },
},
'v27.0.0-alpha.0.0.0': {
semver: { major: 27, raw: '27.0.0-alpha.0.0.0' },
dependencies: { npm: '11.6.0', v8: '14.6.0.0' },
lts: { isLts: false },
alpha: { isAlpha: true },
releaseDate: '2026-10-28',
modules: { version: '150' },
},
},
support: {
phases: {
dates: {
alpha: '2026-10-28',
start: '2027-04-22',
maintenance: '2027-10-20',
end: '2030-04-30',
},
},
},
},
});

assert.equal(result[0]?.status, 'Current');
assert.equal(
result[0]?.minorVersions[1]?.versionWithPrefix,
'v27.0.0-alpha.0.0.0'
);
});

it('uses latest and earliest release dates for latestReleaseDate and initialReleaseDate', async t => {
const result = await runWithNodevuData(t, '2026-04-14', {
26: {
Expand All @@ -138,13 +212,15 @@ describe('generateReleaseData', () => {
semver: { major: 26, raw: '26.2.0' },
dependencies: { npm: '11.3.1', v8: '13.2.20.1' },
lts: { isLts: false },
alpha: { isAlpha: false },
releaseDate: '2026-04-01',
modules: { version: '132' },
},
'26.0.0': {
semver: { major: 26, raw: '26.0.0' },
dependencies: { npm: '11.0.0', v8: '13.1.0.0' },
lts: { isLts: false },
alpha: { isAlpha: false },
releaseDate: '2025-10-21',
modules: { version: '131' },
},
Expand Down
4 changes: 4 additions & 0 deletions apps/site/next-data/generators/releaseData.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ const getNodeReleaseStatus = (latest, eol) => {
return 'LTS';
}

if (latest.alpha.isAlpha) {
return 'Alpha';
}

return 'Current';
};

Expand Down
1 change: 1 addition & 0 deletions apps/site/next.constants.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ export const STATUS_KIND_MAP = {
EOL: 'warning',
LTS: 'info',
Current: 'default',
Alpha: 'default',
};

/**
Expand Down
2 changes: 1 addition & 1 deletion apps/site/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"@node-core/rehype-shiki": "workspace:*",
"@node-core/ui-components": "workspace:*",
"@node-core/website-i18n": "workspace:*",
"@nodevu/core": "0.3.0",
"@nodevu/core": "1.1.0",
"@orama/orama": "^3.1.18",
"@radix-ui/react-tabs": "^1.1.18",
"@radix-ui/react-tooltip": "^1.2.13",
Expand Down
50 changes: 50 additions & 0 deletions apps/site/pages/en/download/alpha.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
layout: download
title: Download Node.js®
---

<section>

Get Node.js® <Release.VersionDropdown /> for <Release.OperatingSystemDropdown /> using <Release.InstallationMethodDropdown /> with <Release.PackageManagerDropdown />

<Release.ReleaseCodeBox />

<Release.Provider>

Or get a prebuilt Node.js® for <Release.OperatingSystemDropdown /> running a <Release.PlatformDropdown /> architecture.

<Release.PrebuiltDownloadButtons />

</Release.Provider>

</section>

<section>

Read the <Release.ChangelogLink>changelog</Release.ChangelogLink> or <Release.BlogPostLink>blog post</Release.BlogPostLink> for this version.

Learn more about [Node.js releases](/about/previous-releases), including the release schedule and LTS status.

Learn how to <LinkWithArrow href="https://github.com/nodejs/node#verifying-binaries">verify</LinkWithArrow> signed SHASUMS.

Looking for Node.js source? Download a signed <Release.DownloadLink kind="source">Node.js source</Release.DownloadLink> tarball.

Check out our <LinkWithArrow href="https://nodejs.org/download/nightly/">nightly</LinkWithArrow> binaries or
all <Link href="/download/archive/current">previous releases</Link>
or the <LinkWithArrow href="https://unofficial-builds.nodejs.org/download/">unofficial</LinkWithArrow> binaries for other platforms.

</section>

---

<section className="mt-3">
<h2 className='text-center'>Proudly supported by the partners below:</h2>

<span className="text-center">
We are able to serve Node.js's downloads and maintain our infrastructure
proudly due to the support of these partners, <Link href="/about/partners">and more</Link>.
</span>
<div className='mt-4'>
<PartnersList size="large" category="infrastructure" length={3} sort="weight" />
</div>
</section>
11 changes: 7 additions & 4 deletions apps/site/scripts/release-post/downloadsTable.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -85,29 +85,32 @@ const resolveUrl = (item, version) => {
const resolveDownloads = version => {
let downloads = downloadOptions;

if (semVer.satisfies(version, '< 16.0.0')) {
const satisfies = range =>
semVer.satisfies(version, range, { includePrerelease: true });

if (satisfies('< 16.0.0')) {
downloads = downloads.filter(
ver => ver.title !== 'macOS Apple Silicon 64-bit Binary'
);
}

if (semVer.satisfies(version, '< 19.9.0')) {
if (satisfies('< 19.9.0')) {
downloads = downloads.filter(
ver =>
ver.title !== 'Windows ARM 64-bit Installer' &&
ver.title !== 'Windows ARM 64-bit Binary'
);
}

if (semVer.satisfies(version, '>= 23.0.0')) {
if (satisfies('>= 23.0.0')) {
downloads = downloads.filter(
ver =>
ver.title !== 'Windows 32-bit Installer' &&
ver.title !== 'Windows 32-bit Binary'
);
}

if (semVer.satisfies(version, '>= 24.0.0')) {
if (satisfies('>= 24.0.0')) {
downloads = downloads.filter(ver => ver.title !== 'ARMv7 32-bit Binary');
}

Expand Down
2 changes: 1 addition & 1 deletion apps/site/types/releases.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type NodeReleaseStatus = 'LTS' | 'Current' | 'EOL';
export type NodeReleaseStatus = 'LTS' | 'Current' | 'Alpha' | 'EOL';

export type NodeReleaseSource = {
major: number;
Expand Down
22 changes: 22 additions & 0 deletions apps/site/util/__tests__/download.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,28 @@ describe('parseCompat', () => {
assert.ok(result[0].disabled);
});

it('should match semver constraints for pre-release versions', () => {
const items = [
{
value: 'testItem',
compatibility: { semver: ['>= 16.9.0'] },
},
{
value: 'testItem2',
compatibility: { semver: ['< 23.0.0'] },
},
];
const result = parseCompat(items, {
os: 'MAC',
installMethod: '',
platform: 'x64',
version: 'v27.0.0-alpha.0.0.0',
release: { status: 'Alpha' },
});
assert.equal(result[0].disabled, false);
assert.ok(result[1].disabled);
});

it('should disable items if release status is not supported', () => {
const items = [
{
Expand Down
4 changes: 3 additions & 1 deletion apps/site/util/download/archive.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ const getCompatiblePlatforms = (
}

return compatibility.semver.every(version =>
semVer.satisfies(versionWithPrefix, version)
semVer.satisfies(versionWithPrefix, version, {
includePrerelease: true,
})
);
})
.map(platform => ({
Expand Down
2 changes: 1 addition & 1 deletion apps/site/util/download/constants.json
Original file line number Diff line number Diff line change
Expand Up @@ -242,5 +242,5 @@
"bitness": ["64", "32"],
"architecture": ["arm", "x86"]
},
"statusOrder": ["Current", "LTS", "EOL"]
"statusOrder": ["Alpha", "Current", "LTS", "EOL"]
}
4 changes: 3 additions & 1 deletion apps/site/util/download/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ export const parseCompat = <
compatibility.installMethod.includes(installMethod),
!compatibility.platform || compatibility.platform.includes(platform),
!compatibility.semver ||
compatibility.semver.every(semver => satisfies(version, semver)),
compatibility.semver.every(semver =>
satisfies(version, semver, { includePrerelease: true })
),
!compatibility.releases ||
compatibility.releases.includes(release.status),
];
Expand Down
1 change: 1 addition & 0 deletions packages/i18n/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@
},
"codeBox": {
"unsupportedVersionWarning": "This version is out of maintenance. Please use a currently supported version. <link>Understand EOL support.</link>",
"alphaVersionWarning": "Alpha releases are meant for early testing and are not intended for production use. Their APIs may change between releases. <link>Learn more about Alpha releases.</link>",
"communityPlatformInfo": "Installation methods that involve community software are supported by the teams maintaining that software.",
"externalSupportInfo": "If you encounter any issues please visit <link>{platform}'s website</link>",
"noScriptDetected": "This page requires JavaScript. You can download Node.js without JavaScript by visiting the <link>downloads archive page</link> directly.",
Expand Down
Loading
Loading