Skip to content

Commit 4f66cee

Browse files
Merge pull request #36320 from storybookjs/fix/node-prerelease-version-gate
CLI: Accept Node.js prerelease builds in the version gate
2 parents 06dc54c + a3a4a53 commit 4f66cee

7 files changed

Lines changed: 46 additions & 44 deletions

File tree

‎code/core/src/bin/dispatcher.ts‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ import { resolvePackageDir } from '../shared/utils/module.ts';
2323
* - Init is routed to the create-storybook package via the detected package manager
2424
* - External CLI tools (upgrade, doctor, etc.) are routed to @storybook/cli the same way
2525
*/
26-
const [major, minor, patch] = process.versions.node.split('.').map(Number);
27-
if (!isNodeVersionSupported(major, minor, patch)) {
26+
if (!isNodeVersionSupported(process.versions.node)) {
2827
logger.error(
2928
dedent`To run Storybook, you need Node.js version ${MIN_SUPPORTED_NODE_DESCRIPTION}.
3029
You are currently running Node.js ${process.version}. Please upgrade your Node.js installation.`

‎code/core/src/common/node-version.test.ts‎

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,48 +29,52 @@ describe('node-version', () => {
2929
});
3030

3131
describe('isNodeVersionSupported', () => {
32-
it('rejects 20.19.0 (below minimum major)', () => {
33-
expect(isNodeVersionSupported(20, 19, 0)).toBe(false);
32+
it('accepts exact minimum version (22.12.0)', () => {
33+
expect(isNodeVersionSupported('22.12.0')).toBe(true);
3434
});
3535

36-
it('accepts exact minimum version (22.12.0)', () => {
37-
expect(isNodeVersionSupported(22, 12, 0)).toBe(true);
36+
it('accepts 22.12.1 (patch above minimum)', () => {
37+
expect(isNodeVersionSupported('22.12.1')).toBe(true);
3838
});
3939

40-
it('rejects version below minimum minor (20.18.0)', () => {
41-
expect(isNodeVersionSupported(20, 18, 0)).toBe(false);
40+
it('accepts future major above highest defined (24.0.0)', () => {
41+
expect(isNodeVersionSupported('24.0.0')).toBe(true);
4242
});
4343

44-
it('rejects version below minimum major (18.0.0)', () => {
45-
expect(isNodeVersionSupported(18, 0, 0)).toBe(false);
44+
it('accepts prerelease of a supported version (26.1.0-rc.0)', () => {
45+
expect(isNodeVersionSupported('26.1.0-rc.0')).toBe(true);
4646
});
4747

48-
it('rejects 20.20.0 (below minimum major)', () => {
49-
expect(isNodeVersionSupported(20, 20, 0)).toBe(false);
48+
it('accepts prerelease of a future major (27.0.0-alpha.1)', () => {
49+
expect(isNodeVersionSupported('27.0.0-alpha.1')).toBe(true);
5050
});
5151

52-
it('rejects 21.0.0 (below minimum major)', () => {
53-
expect(isNodeVersionSupported(21, 0, 0)).toBe(false);
52+
it('accepts nightly builds (26.0.0-nightly20260915abc1234)', () => {
53+
expect(isNodeVersionSupported('26.0.0-nightly20260915abc1234')).toBe(true);
5454
});
5555

56-
it('accepts future major above highest defined (24.0.0)', () => {
57-
expect(isNodeVersionSupported(24, 0, 0)).toBe(true);
56+
it('rejects a prerelease below the minimum (22.12.0-rc.0)', () => {
57+
expect(isNodeVersionSupported('22.12.0-rc.0')).toBe(false);
5858
});
5959

6060
it('rejects 22.11.0 (one minor below 22.12)', () => {
61-
expect(isNodeVersionSupported(22, 11, 0)).toBe(false);
61+
expect(isNodeVersionSupported('22.11.0')).toBe(false);
6262
});
6363

64-
it('accepts 22.12.1 (patch above minimum)', () => {
65-
expect(isNodeVersionSupported(22, 12, 1)).toBe(true);
64+
it('rejects 20.19.0 (below minimum major)', () => {
65+
expect(isNodeVersionSupported('20.19.0')).toBe(false);
66+
});
67+
68+
it('rejects 21.0.0 (below minimum major)', () => {
69+
expect(isNodeVersionSupported('21.0.0')).toBe(false);
6670
});
6771

68-
it('rejects 19.99.99', () => {
69-
expect(isNodeVersionSupported(19, 99, 99)).toBe(false);
72+
it('rejects 18.0.0 (below minimum major)', () => {
73+
expect(isNodeVersionSupported('18.0.0')).toBe(false);
7074
});
7175

72-
it('rejects major-only "22" (treated as 22.0.0 which is below 22.12)', () => {
73-
expect(isNodeVersionSupported(22, 0, 0)).toBe(false);
76+
it('fails closed for unparseable input (not-a-version)', () => {
77+
expect(isNodeVersionSupported('not-a-version')).toBe(false);
7478
});
7579
});
7680

‎code/core/src/common/node-version.ts‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,13 @@ export const MIN_SUPPORTED_NODE_DESCRIPTION =
3535
MIN_SUPPORTED_NODE_VERSIONS.map(formatMinVersion).join(' or ');
3636

3737
/**
38-
* Check whether a Node.js version (major.minor.patch) meets the minimum requirement.
38+
* Check whether a Node.js version meets the minimum requirement.
3939
*
40-
* Missing version components should be normalized by callers (e.g. "22" -> 22.0.0).
40+
* Expects the raw `process.versions.node` value (e.g. "22.12.0", "26.1.0-rc.0").
41+
* Prerelease builds are evaluated with semver's prerelease rules, so a prerelease
42+
* below the floor (e.g. "22.12.0-rc.0") still fails. Unparseable input fails closed.
4143
*/
42-
export function isNodeVersionSupported(major: number, minor: number, patch: number): boolean {
44+
export function isNodeVersionSupported(version: string): boolean {
4345
const sortedMinimums = [...MIN_SUPPORTED_NODE_VERSIONS].sort((a, b) => a.major - b.major);
4446
const supportedRange = sortedMinimums
4547
.map((min, index) => {
@@ -54,5 +56,5 @@ export function isNodeVersionSupported(major: number, minor: number, patch: numb
5456
})
5557
.join(' || ');
5658

57-
return satisfies(`${major}.${minor}.${patch}`, supportedRange);
59+
return satisfies(version, supportedRange, { includePrerelease: true });
5860
}

‎code/lib/cli-storybook/src/autoblock/block-node-version.test.ts‎

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,16 @@ describe('minimumNode22 blocker', () => {
1414
stubNodeVersion(originalNodeVersion);
1515
});
1616

17-
test.each(['20.19.0', '21.7.3', '22.11.0'])('blocks Node.js %s', async (version) => {
18-
stubNodeVersion(version);
19-
const options = {} as AutoblockOptions;
20-
await expect(blocker.check(options)).resolves.toEqual({ nodeVersion: version });
21-
});
22-
23-
test.each(['22.12.0', '24.1.0'])('does not block Node.js %s', async (version) => {
17+
test.each(['20.19.0', '21.7.3', '22.11.0', '20.0.0-rc.0', '22.12.0-rc.1'])(
18+
'blocks Node.js %s',
19+
async (version) => {
20+
stubNodeVersion(version);
21+
const options = {} as AutoblockOptions;
22+
await expect(blocker.check(options)).resolves.toEqual({ nodeVersion: version });
23+
}
24+
);
25+
26+
test.each(['22.12.0', '24.1.0', '22.13.0-rc.1'])('does not block Node.js %s', async (version) => {
2427
stubNodeVersion(version);
2528
const options = {} as AutoblockOptions;
2629
await expect(blocker.check(options)).resolves.toBe(false);

‎code/lib/cli-storybook/src/autoblock/block-node-version.ts‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,8 @@ export const blocker = createBlocker({
66
id: 'minimumNode22',
77
async check() {
88
const nodeVersion = process.versions.node;
9-
if (nodeVersion) {
10-
const [major, minor, patch] = nodeVersion.split('.').map(Number);
11-
if (!isNodeVersionSupported(major, minor, patch)) {
12-
return { nodeVersion };
13-
}
9+
if (nodeVersion && !isNodeVersionSupported(nodeVersion)) {
10+
return { nodeVersion };
1411
}
1512
return false;
1613
},

‎code/lib/cli-storybook/src/bin/index.ts‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ import { logger } from 'storybook/internal/node-logger';
44

55
import { dedent } from 'ts-dedent';
66

7-
const [major, minor, patch] = process.versions.node.split('.').map(Number);
8-
9-
if (!isNodeVersionSupported(major, minor, patch)) {
7+
if (!isNodeVersionSupported(process.versions.node)) {
108
logger.error(
119
dedent`To run Storybook, you need Node.js version ${MIN_SUPPORTED_NODE_DESCRIPTION}.
1210
You are currently running Node.js ${process.version}. Please upgrade your Node.js installation.`

‎code/lib/create-storybook/src/bin/index.ts‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22
export {};
33

44
const { isNodeVersionSupported } = await import('storybook/internal/common');
5-
const [major, minor, patch] = process.versions.node.split('.').map(Number);
65

7-
if (!isNodeVersionSupported(major, minor, patch)) {
6+
if (!isNodeVersionSupported(process.versions.node)) {
87
const { MIN_SUPPORTED_NODE_DESCRIPTION } = await import('storybook/internal/common');
98
const { logger } = await import('storybook/internal/node-logger');
109
logger.error(

0 commit comments

Comments
 (0)