From 69f9538081c7ed85d68d3d574fef12a15255b3a0 Mon Sep 17 00:00:00 2001 From: jycouet Date: Sun, 29 Jun 2025 21:06:10 +0200 Subject: [PATCH 1/7] feat(add): Add minimum Node.js version warning if below 16. --- .changeset/great-bags-smoke.md | 5 +++++ packages/cli/package.json | 2 ++ packages/cli/utils/common.ts | 9 +++++++++ pnpm-lock.yaml | 6 ++++++ 4 files changed, 22 insertions(+) create mode 100644 .changeset/great-bags-smoke.md diff --git a/.changeset/great-bags-smoke.md b/.changeset/great-bags-smoke.md new file mode 100644 index 00000000..97529e4b --- /dev/null +++ b/.changeset/great-bags-smoke.md @@ -0,0 +1,5 @@ +--- +'sv': patch +--- + +feat(add): Add minimum Node.js version warning if below 16. diff --git a/packages/cli/package.json b/packages/cli/package.json index 87a2864f..40a38a92 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -36,12 +36,14 @@ "@sveltejs/create": "workspace:*", "@types/degit": "^2.8.6", "@types/ps-tree": "^1.1.6", + "@types/semver": "^7.7.0", "commander": "^13.1.0", "degit": "^2.8.4", "empathic": "^1.1.0", "package-manager-detector": "^0.2.11", "picocolors": "^1.1.1", "ps-tree": "^1.2.0", + "semver": "^7.7.2", "tinyexec": "^0.3.2", "valibot": "^0.41.0" }, diff --git a/packages/cli/utils/common.ts b/packages/cli/utils/common.ts index b2282f97..83e32fe0 100644 --- a/packages/cli/utils/common.ts +++ b/packages/cli/utils/common.ts @@ -4,6 +4,7 @@ import * as p from '@clack/prompts'; import type { Argument, HelpConfiguration, Option } from 'commander'; import { UnsupportedError } from './errors.ts'; import process from 'node:process'; +import semver from 'semver'; const NO_PREFIX = '--no-'; let options: readonly Option[] = []; @@ -75,6 +76,14 @@ type MaybePromise = () => Promise | void; export async function runCommand(action: MaybePromise): Promise { try { p.intro(`Welcome to the Svelte CLI! ${pc.gray(`(v${pkg.version})`)}`); + + const isUnSupportedNodeVersion = + process.versions.node && semver.lt(process.versions.node, '16.0.0'); + if (isUnSupportedNodeVersion) { + p.log.warn( + `You are using Node.js ${pc.red(process.versions.node)}, please upgrade to Node.js 16 or higher.` + ); + } await action(); p.outro("You're all set!"); } catch (e) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 959d7df3..f7a170ef 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -115,6 +115,9 @@ importers: '@types/ps-tree': specifier: ^1.1.6 version: 1.1.6 + '@types/semver': + specifier: ^7.7.0 + version: 7.7.0 commander: specifier: ^13.1.0 version: 13.1.0 @@ -133,6 +136,9 @@ importers: ps-tree: specifier: ^1.2.0 version: 1.2.0 + semver: + specifier: ^7.7.2 + version: 7.7.2 tinyexec: specifier: ^0.3.2 version: 0.3.2 From 42976934bf3db3a5504b41d7b90eb24fafed5a75 Mon Sep 17 00:00:00 2001 From: jycouet Date: Sun, 29 Jun 2025 21:17:17 +0200 Subject: [PATCH 2/7] new line* --- packages/cli/utils/common.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cli/utils/common.ts b/packages/cli/utils/common.ts index 83e32fe0..2662a253 100644 --- a/packages/cli/utils/common.ts +++ b/packages/cli/utils/common.ts @@ -84,6 +84,7 @@ export async function runCommand(action: MaybePromise): Promise { `You are using Node.js ${pc.red(process.versions.node)}, please upgrade to Node.js 16 or higher.` ); } + await action(); p.outro("You're all set!"); } catch (e) { From d5126d3340743d9f015db57ee1b85155b1846115 Mon Sep 17 00:00:00 2001 From: jycouet Date: Sat, 5 Jul 2025 10:05:02 +0200 Subject: [PATCH 3/7] add version helper in code to be used everywhere (+ tests) --- packages/core/common.ts | 57 +++++++++++++++++++++++++++++++++++ packages/core/index.ts | 1 + packages/core/tests/common.ts | 50 ++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 packages/core/common.ts create mode 100644 packages/core/tests/common.ts diff --git a/packages/core/common.ts b/packages/core/common.ts new file mode 100644 index 00000000..64d0a5c0 --- /dev/null +++ b/packages/core/common.ts @@ -0,0 +1,57 @@ +type Version = { + major?: number; + minor?: number; + patch?: number; +}; + +export function versionSplit(str: string): Version { + const [major, minor, patch] = str?.split('.') ?? []; + + function toVersionNumber(val: string | undefined): number | undefined { + return val !== undefined && val !== '' && !isNaN(Number(val)) ? Number(val) : undefined; + } + + return { + major: toVersionNumber(major), + minor: toVersionNumber(minor), + patch: toVersionNumber(patch) + }; +} + +function versionUnsupportedBelow(version_str: string, below_str: string): boolean | undefined { + const version = versionSplit(version_str); + const below = versionSplit(below_str); + + if (version.major === undefined || below.major === undefined) return undefined; + if (version.major < below.major) return true; + if (version.major > below.major) return false; + + if (version.minor === undefined || below.minor === undefined) { + if (version.major === below.major) return false; + else return true; + } + if (version.minor < below.minor) return true; + if (version.minor > below.minor) return false; + + if (version.patch === undefined || below.patch === undefined) { + if (version.minor === below.minor) return false; + else return true; + } + if (version.patch < below.patch) return true; + if (version.patch > below.patch) return false; + if (version.patch === below.patch) return false; + + return undefined; +} + +/** + * @example + * const unsupported = minimumRequirement('18.3').for(process.versions.node); + */ +export function minimumRequirement(version: string): { + for: (target: string) => boolean | undefined; +} { + return { + for: (target: string) => versionUnsupportedBelow(target, version) + }; +} diff --git a/packages/core/index.ts b/packages/core/index.ts index e22448cb..90d3fa07 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -3,6 +3,7 @@ export { log } from '@clack/prompts'; export { default as colors } from 'picocolors'; export { default as dedent } from 'dedent'; export * as utils from './utils.ts'; +export { minimumRequirement, versionSplit } from './common.ts'; export type * from './addon/processors.ts'; export type * from './addon/options.ts'; diff --git a/packages/core/tests/common.ts b/packages/core/tests/common.ts new file mode 100644 index 00000000..fdf6be33 --- /dev/null +++ b/packages/core/tests/common.ts @@ -0,0 +1,50 @@ +import { expect, describe, it } from 'vitest'; +import { versionSplit, minimumRequirement } from '../common.ts'; + +describe('versionSplit', () => { + const combinationsVersionSplit = [ + { version: '18.13.0', expected: { major: 18, minor: 13, patch: 0 } }, + { version: 'x.13.0', expected: { major: undefined, minor: 13, patch: 0 } }, + { version: '18.y.0', expected: { major: 18, minor: undefined, patch: 0 } }, + { version: '18.13.z', expected: { major: 18, minor: 13, patch: undefined } }, + { version: '18', expected: { major: 18, minor: undefined, patch: undefined } }, + { version: '18.13', expected: { major: 18, minor: 13, patch: undefined } }, + { version: 'invalid', expected: { major: undefined, minor: undefined, patch: undefined } } + ]; + it.each(combinationsVersionSplit)( + 'should return the correct version for $version', + ({ version, expected }) => { + expect(versionSplit(version)).toEqual(expected); + } + ); +}); + +describe('minimumRequirement', () => { + const combinationsMinimumRequirement = [ + { version: '17', below: '18.3.0', expected: true }, + { version: '18.2', below: '18.3.0', expected: true }, + { version: '18.3.0', below: '18.3.1', expected: true }, + { version: '18.3.1', below: '18.3.0', expected: false }, + { version: '18.3.0', below: '18.3.0', expected: false }, + { version: '18.3.0', below: '18.3', expected: false }, + { version: '18.3.1', below: '18.3', expected: false }, + { version: '18.3.1', below: '18', expected: false }, + { version: '18', below: '18', expected: false }, + { version: 'a', below: 'b', expected: undefined }, + { version: '18.3', below: '18.3', expected: false }, + { version: '18.4', below: '18.3', expected: false }, + { version: '18.2', below: '18.3', expected: true }, + + // if it's undefined, we can't say anything... + { version: undefined!, below: '18.3', expected: undefined }, + { version: '', below: '18.3', expected: undefined } + ] as const; + it.each(combinationsMinimumRequirement)( + '($version below $below) should be $expected', + ({ version, below, expected }) => { + expect(minimumRequirement(below).for(version)).toEqual(expected); + } + ); +}); + +// minimumRequirement(4.0).for(nodeVersion) From 936c0b8397b4fabda44330876a7cf5b25d1d819f Mon Sep 17 00:00:00 2001 From: jycouet Date: Sat, 5 Jul 2025 10:05:17 +0200 Subject: [PATCH 4/7] rmv semver in cli --- packages/cli/package.json | 2 -- pnpm-lock.yaml | 6 ------ 2 files changed, 8 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 40a38a92..87a2864f 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -36,14 +36,12 @@ "@sveltejs/create": "workspace:*", "@types/degit": "^2.8.6", "@types/ps-tree": "^1.1.6", - "@types/semver": "^7.7.0", "commander": "^13.1.0", "degit": "^2.8.4", "empathic": "^1.1.0", "package-manager-detector": "^0.2.11", "picocolors": "^1.1.1", "ps-tree": "^1.2.0", - "semver": "^7.7.2", "tinyexec": "^0.3.2", "valibot": "^0.41.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f7a170ef..959d7df3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -115,9 +115,6 @@ importers: '@types/ps-tree': specifier: ^1.1.6 version: 1.1.6 - '@types/semver': - specifier: ^7.7.0 - version: 7.7.0 commander: specifier: ^13.1.0 version: 13.1.0 @@ -136,9 +133,6 @@ importers: ps-tree: specifier: ^1.2.0 version: 1.2.0 - semver: - specifier: ^7.7.2 - version: 7.7.2 tinyexec: specifier: ^0.3.2 version: 0.3.2 From 90da3ff07199eeab89b74ccd697487565a51a166 Mon Sep 17 00:00:00 2001 From: jycouet Date: Sat, 5 Jul 2025 10:05:43 +0200 Subject: [PATCH 5/7] using minimumRequirement 18.3 for node --- packages/cli/utils/common.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/cli/utils/common.ts b/packages/cli/utils/common.ts index 2662a253..857850f5 100644 --- a/packages/cli/utils/common.ts +++ b/packages/cli/utils/common.ts @@ -4,7 +4,7 @@ import * as p from '@clack/prompts'; import type { Argument, HelpConfiguration, Option } from 'commander'; import { UnsupportedError } from './errors.ts'; import process from 'node:process'; -import semver from 'semver'; +import { minimumRequirement } from '@sveltejs/cli-core'; const NO_PREFIX = '--no-'; let options: readonly Option[] = []; @@ -77,11 +77,10 @@ export async function runCommand(action: MaybePromise): Promise { try { p.intro(`Welcome to the Svelte CLI! ${pc.gray(`(v${pkg.version})`)}`); - const isUnSupportedNodeVersion = - process.versions.node && semver.lt(process.versions.node, '16.0.0'); - if (isUnSupportedNodeVersion) { + const unsupported = minimumRequirement('18.3').for(process.versions.node); + if (unsupported) { p.log.warn( - `You are using Node.js ${pc.red(process.versions.node)}, please upgrade to Node.js 16 or higher.` + `You are using Node.js ${pc.red(process.versions.node)}, please upgrade to Node.js 18.3 or higher.` ); } From 021d3d909a5c946c5d9f64e57c018b8303515f9a Mon Sep 17 00:00:00 2001 From: "jyc.dev" Date: Thu, 10 Jul 2025 20:02:52 +0200 Subject: [PATCH 6/7] Update .changeset with 18.3 Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> --- .changeset/great-bags-smoke.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/great-bags-smoke.md b/.changeset/great-bags-smoke.md index 97529e4b..1ad40d6b 100644 --- a/.changeset/great-bags-smoke.md +++ b/.changeset/great-bags-smoke.md @@ -2,4 +2,4 @@ 'sv': patch --- -feat(add): Add minimum Node.js version warning if below 16. +feat: print warning if using Node.js version below 18.3 From babeec52a41bf387bc9163c89b21184f8df08f62 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Fri, 18 Jul 2025 15:22:24 +0200 Subject: [PATCH 7/7] minor improvements --- packages/cli/utils/common.ts | 7 ++++--- packages/core/common.ts | 23 +++++++---------------- packages/core/index.ts | 2 +- packages/core/tests/common.ts | 8 +++----- 4 files changed, 15 insertions(+), 25 deletions(-) diff --git a/packages/cli/utils/common.ts b/packages/cli/utils/common.ts index 857850f5..c8d86a23 100644 --- a/packages/cli/utils/common.ts +++ b/packages/cli/utils/common.ts @@ -4,7 +4,7 @@ import * as p from '@clack/prompts'; import type { Argument, HelpConfiguration, Option } from 'commander'; import { UnsupportedError } from './errors.ts'; import process from 'node:process'; -import { minimumRequirement } from '@sveltejs/cli-core'; +import { isVersionUnsupportedBelow } from '@sveltejs/cli-core'; const NO_PREFIX = '--no-'; let options: readonly Option[] = []; @@ -77,10 +77,11 @@ export async function runCommand(action: MaybePromise): Promise { try { p.intro(`Welcome to the Svelte CLI! ${pc.gray(`(v${pkg.version})`)}`); - const unsupported = minimumRequirement('18.3').for(process.versions.node); + const minimumVersion = '18.3.0'; + const unsupported = isVersionUnsupportedBelow(process.versions.node, minimumVersion); if (unsupported) { p.log.warn( - `You are using Node.js ${pc.red(process.versions.node)}, please upgrade to Node.js 18.3 or higher.` + `You are using Node.js ${pc.red(process.versions.node)}, please upgrade to Node.js ${pc.green(minimumVersion)} or higher.` ); } diff --git a/packages/core/common.ts b/packages/core/common.ts index 64d0a5c0..dfdd50fc 100644 --- a/packages/core/common.ts +++ b/packages/core/common.ts @@ -4,7 +4,7 @@ type Version = { patch?: number; }; -export function versionSplit(str: string): Version { +export function splitVersion(str: string): Version { const [major, minor, patch] = str?.split('.') ?? []; function toVersionNumber(val: string | undefined): number | undefined { @@ -18,9 +18,12 @@ export function versionSplit(str: string): Version { }; } -function versionUnsupportedBelow(version_str: string, below_str: string): boolean | undefined { - const version = versionSplit(version_str); - const below = versionSplit(below_str); +export function isVersionUnsupportedBelow( + versionStr: string, + belowStr: string +): boolean | undefined { + const version = splitVersion(versionStr); + const below = splitVersion(belowStr); if (version.major === undefined || below.major === undefined) return undefined; if (version.major < below.major) return true; @@ -43,15 +46,3 @@ function versionUnsupportedBelow(version_str: string, below_str: string): boolea return undefined; } - -/** - * @example - * const unsupported = minimumRequirement('18.3').for(process.versions.node); - */ -export function minimumRequirement(version: string): { - for: (target: string) => boolean | undefined; -} { - return { - for: (target: string) => versionUnsupportedBelow(target, version) - }; -} diff --git a/packages/core/index.ts b/packages/core/index.ts index 90d3fa07..92fed5cf 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -3,7 +3,7 @@ export { log } from '@clack/prompts'; export { default as colors } from 'picocolors'; export { default as dedent } from 'dedent'; export * as utils from './utils.ts'; -export { minimumRequirement, versionSplit } from './common.ts'; +export { isVersionUnsupportedBelow, splitVersion } from './common.ts'; export type * from './addon/processors.ts'; export type * from './addon/options.ts'; diff --git a/packages/core/tests/common.ts b/packages/core/tests/common.ts index fdf6be33..100f7ad9 100644 --- a/packages/core/tests/common.ts +++ b/packages/core/tests/common.ts @@ -1,5 +1,5 @@ import { expect, describe, it } from 'vitest'; -import { versionSplit, minimumRequirement } from '../common.ts'; +import { splitVersion, isVersionUnsupportedBelow } from '../common.ts'; describe('versionSplit', () => { const combinationsVersionSplit = [ @@ -14,7 +14,7 @@ describe('versionSplit', () => { it.each(combinationsVersionSplit)( 'should return the correct version for $version', ({ version, expected }) => { - expect(versionSplit(version)).toEqual(expected); + expect(splitVersion(version)).toEqual(expected); } ); }); @@ -42,9 +42,7 @@ describe('minimumRequirement', () => { it.each(combinationsMinimumRequirement)( '($version below $below) should be $expected', ({ version, below, expected }) => { - expect(minimumRequirement(below).for(version)).toEqual(expected); + expect(isVersionUnsupportedBelow(version, below)).toEqual(expected); } ); }); - -// minimumRequirement(4.0).for(nodeVersion)