diff --git a/packages/create-snap/.depcheckrc.json b/packages/create-snap/.depcheckrc.json index 15d64e734b..c075cbea8e 100644 --- a/packages/create-snap/.depcheckrc.json +++ b/packages/create-snap/.depcheckrc.json @@ -5,6 +5,7 @@ "@lavamoat/preinstall-always-fail", "@metamask/auto-changelog", "@metamask/eslint-*", + "@metamask/create-snap", "@types/*", "@typescript-eslint/*", "eslint-config-*", diff --git a/packages/create-snap/package.json b/packages/create-snap/package.json index ff6d7f41d9..9c5320c28e 100644 --- a/packages/create-snap/package.json +++ b/packages/create-snap/package.json @@ -47,7 +47,7 @@ }, "dependencies": { "@metamask/snaps-utils": "workspace:^", - "@metamask/utils": "^8.3.0", + "semver": "^7.5.4", "yargs": "^17.7.1" }, "devDependencies": { diff --git a/packages/create-snap/src/cmds/init/initHandler.test.ts b/packages/create-snap/src/cmds/init/initHandler.test.ts index 9b6c8f3b47..27262933f0 100644 --- a/packages/create-snap/src/cmds/init/initHandler.test.ts +++ b/packages/create-snap/src/cmds/init/initHandler.test.ts @@ -4,9 +4,9 @@ import { getMockSnapFiles, getMockSnapFilesWithUpdatedChecksum, } from '@metamask/snaps-utils/test-utils'; -import * as utils from '@metamask/utils'; import { promises as fs } from 'fs'; import pathUtils from 'path'; +import semver from 'semver'; import { resetFileSystem } from '../../test-utils'; import type { YargsArgs } from '../../types/yargs'; @@ -15,9 +15,9 @@ import * as initUtils from './initUtils'; jest.mock('fs'); -jest.mock('@metamask/utils', () => ({ - ...jest.requireActual('@metamask/utils'), - satisfiesVersionRange: jest.fn(), +jest.mock('semver', () => ({ + ...jest.requireActual('semver'), + satisfies: jest.fn(), })); jest.mock('@metamask/snaps-utils', () => ({ @@ -47,7 +47,7 @@ describe('initialize', () => { }); it('successfully initializes a Snap project in the current working directory', async () => { - jest.spyOn(utils, 'satisfiesVersionRange').mockImplementation(() => true); + jest.spyOn(semver, 'satisfies').mockImplementation(() => true); jest.spyOn(initUtils, 'isGitInstalled').mockImplementation(() => true); jest.spyOn(initUtils, 'cloneTemplate').mockImplementation(); @@ -82,7 +82,7 @@ describe('initialize', () => { }); it('successfully initializes a Snap project in a given directory', async () => { - jest.spyOn(utils, 'satisfiesVersionRange').mockImplementation(() => true); + jest.spyOn(semver, 'satisfies').mockImplementation(() => true); jest.spyOn(initUtils, 'isGitInstalled').mockImplementation(() => true); @@ -123,7 +123,7 @@ describe('initialize', () => { }); it("defaults to 'src/index.js' if there is no main entry in package.json", async () => { - jest.spyOn(utils, 'satisfiesVersionRange').mockImplementation(() => true); + jest.spyOn(semver, 'satisfies').mockImplementation(() => true); jest.spyOn(initUtils, 'isGitInstalled').mockImplementation(() => true); jest.spyOn(initUtils, 'cloneTemplate').mockImplementation(); @@ -163,7 +163,7 @@ describe('initialize', () => { }); it("doesn't init if it's already in a git repo", async () => { - jest.spyOn(utils, 'satisfiesVersionRange').mockImplementation(() => true); + jest.spyOn(semver, 'satisfies').mockImplementation(() => true); jest.spyOn(initUtils, 'isGitInstalled').mockImplementation(() => true); jest.spyOn(initUtils, 'cloneTemplate').mockImplementation(); @@ -212,12 +212,12 @@ describe('initialize', () => { }; await expect(initHandler({ ...getMockArgv() })).rejects.toThrow( - `Init Error: You are using an outdated version of Node (${process.version}). Please update to Node >=18.6.0.`, + `Init Error: You are using an outdated version of Node (${process.version}). Please update to Node 18.16.0 or later.`, ); }); it('fails if git is not installed', async () => { - jest.spyOn(utils, 'satisfiesVersionRange').mockImplementation(() => true); + jest.spyOn(semver, 'satisfies').mockImplementation(() => true); const isGitInstalledMock = jest .spyOn(initUtils, 'isGitInstalled') @@ -231,7 +231,7 @@ describe('initialize', () => { }); it('fails if it can\t clone template and clean files', async () => { - jest.spyOn(utils, 'satisfiesVersionRange').mockImplementation(() => true); + jest.spyOn(semver, 'satisfies').mockImplementation(() => true); jest.spyOn(initUtils, 'isGitInstalled').mockImplementation(() => true); @@ -249,7 +249,7 @@ describe('initialize', () => { }); it('fails if an error is thrown', async () => { - jest.spyOn(utils, 'satisfiesVersionRange').mockImplementation(() => true); + jest.spyOn(semver, 'satisfies').mockImplementation(() => true); jest.spyOn(initUtils, 'isGitInstalled').mockImplementation(() => true); diff --git a/packages/create-snap/src/cmds/init/initHandler.ts b/packages/create-snap/src/cmds/init/initHandler.ts index d1ff620d47..ed55ffee78 100644 --- a/packages/create-snap/src/cmds/init/initHandler.ts +++ b/packages/create-snap/src/cmds/init/initHandler.ts @@ -1,3 +1,5 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +import cliPackageJson from '@metamask/create-snap/package.json'; import type { NpmSnapPackageJson } from '@metamask/snaps-utils'; import { NpmSnapFileNames, @@ -5,10 +7,10 @@ import { createSnapManifest, logInfo, } from '@metamask/snaps-utils/node'; -import type { SemVerRange, SemVerVersion } from '@metamask/utils'; -import { satisfiesVersionRange } from '@metamask/utils'; import { promises as fs } from 'fs'; import pathUtils from 'path'; +import type { SemVer } from 'semver'; +import semver from 'semver'; import type { YargsArgs } from '../../types/yargs'; import { @@ -22,8 +24,6 @@ import { yarnInstall, } from './initUtils'; -const SATISFIED_VERSION = '>=18.6.0' as SemVerRange; - /** * Creates a new snap package, based on one of the provided templates. This * creates all the necessary files, like `package.json`, `snap.config.js`, etc. @@ -37,14 +37,14 @@ const SATISFIED_VERSION = '>=18.6.0' as SemVerRange; export async function initHandler(argv: YargsArgs) { const { directory } = argv; - const isVersionSupported = satisfiesVersionRange( - process.version as SemVerVersion, - SATISFIED_VERSION, - ); + const versionRange = cliPackageJson.engines.node; + const minimumVersion = (semver.minVersion(versionRange) as SemVer).format(); + + const isVersionSupported = semver.satisfies(process.version, versionRange); if (!isVersionSupported) { throw new Error( - `Init Error: You are using an outdated version of Node (${process.version}). Please update to Node ${SATISFIED_VERSION}.`, + `Init Error: You are using an outdated version of Node (${process.version}). Please update to Node ${minimumVersion} or later.`, ); } diff --git a/packages/create-snap/tsup.config.ts b/packages/create-snap/tsup.config.ts index eda54c1cae..d62dc9943a 100644 --- a/packages/create-snap/tsup.config.ts +++ b/packages/create-snap/tsup.config.ts @@ -9,6 +9,7 @@ const { default: baseConfig } = require('../../tsup.config'); const config: Options = { name: packageJson.name, + external: ['@metamask/create-snap'], platform: 'node', }; diff --git a/packages/snaps-cli/src/cli.test.ts b/packages/snaps-cli/src/cli.test.ts index 01feb9602c..b564694f6b 100644 --- a/packages/snaps-cli/src/cli.test.ts +++ b/packages/snaps-cli/src/cli.test.ts @@ -28,7 +28,7 @@ const getMockArgv = (...args: string[]) => { const HELP_TEXT_REGEX = /^\s*Usage: .+ \[options\]/u; describe('checkNodeVersion', () => { - it.each(['16.17.0', '16.18.0', '18.6.0', '18.7.0', '20.0.0'])( + it.each(['18.16.0', '18.17.0', '20.0.0'])( 'does not exit if the Node version is %s', (version) => { const spy = jest.spyOn(process, 'exit').mockImplementation(); @@ -38,8 +38,8 @@ describe('checkNodeVersion', () => { }, ); - it.each(['14.0.0', '16.0.0', '16.16.1'])( - 'logs a message and exists if the Node version is %s', + it.each(['14.0.0', '16.0.0', '16.16.1', '18.0.0', '18.5.0'])( + 'logs a message and exits if the Node version is %s', (version) => { const spy = jest.spyOn(process, 'exit').mockImplementation(); const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); @@ -51,26 +51,7 @@ describe('checkNodeVersion', () => { expect(consoleErrorSpy).toHaveBeenCalledTimes(1); expect(consoleErrorSpy).toHaveBeenCalledWith( expect.stringContaining( - `Node version ${version} is not supported. Please use Node 16.17.0 or later.`, - ), - ); - }, - ); - - it.each(['18.0.0', '18.5.0'])( - 'logs a message and exists if the Node version is %s', - (version) => { - const spy = jest.spyOn(process, 'exit').mockImplementation(); - const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); - - checkNodeVersion(version); - - expect(spy).toHaveBeenCalledTimes(1); - expect(spy).toHaveBeenCalledWith(1); - expect(consoleErrorSpy).toHaveBeenCalledTimes(1); - expect(consoleErrorSpy).toHaveBeenCalledWith( - expect.stringContaining( - `Node version ${version} is not supported. Please use Node 18.6.0 or later.`, + `Node version ${version} is not supported. Please use Node 18.16.0 or later.`, ), ); }, diff --git a/packages/snaps-cli/src/cli.ts b/packages/snaps-cli/src/cli.ts old mode 100755 new mode 100644 index 0a8b6567f8..180c37ed20 --- a/packages/snaps-cli/src/cli.ts +++ b/packages/snaps-cli/src/cli.ts @@ -1,3 +1,6 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +import packageJson from '@metamask/snaps-cli/package.json'; +import type { SemVer } from 'semver'; import semver from 'semver'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; @@ -6,9 +9,6 @@ import builders from './builders'; import { getConfigByArgv } from './config'; import { error, getYargsErrorMessage, sanitizeInputs } from './utils'; -const MINIMUM_NODE_16_VERSION = '16.17.0'; -const MINIMUM_NODE_18_VERSION = '18.6.0'; - /** * Check the Node version. If the Node version is less than the minimum required * version, this logs an error and exits the process. @@ -18,26 +18,12 @@ const MINIMUM_NODE_18_VERSION = '18.6.0'; export function checkNodeVersion( nodeVersion: string = process.version.slice(1), ) { - const majorVersion = semver.major(nodeVersion); - const message = `Node version ${nodeVersion} is not supported. Please use Node ${MINIMUM_NODE_16_VERSION} or later.`; - - if (majorVersion < 16) { - error(message); - // eslint-disable-next-line n/no-process-exit - process.exit(1); - } - - // Node 16 and 18 have a different minimum version requirement, so we need to - // check for both. - if (majorVersion === 16 && semver.lt(nodeVersion, MINIMUM_NODE_16_VERSION)) { - error(message); - // eslint-disable-next-line n/no-process-exit - process.exit(1); - } + const versionRange = packageJson.engines.node; + const minimumVersion = (semver.minVersion(versionRange) as SemVer).format(); - if (majorVersion === 18 && semver.lt(nodeVersion, MINIMUM_NODE_18_VERSION)) { + if (!semver.satisfies(nodeVersion, versionRange)) { error( - `Node version ${nodeVersion} is not supported. Please use Node ${MINIMUM_NODE_18_VERSION} or later.`, + `Node version ${nodeVersion} is not supported. Please use Node ${minimumVersion} or later.`, ); // eslint-disable-next-line n/no-process-exit process.exit(1); diff --git a/yarn.lock b/yarn.lock index da8e756902..81981fb1dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4205,7 +4205,6 @@ __metadata: "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 "@metamask/snaps-utils": "workspace:^" - "@metamask/utils": ^8.3.0 "@swc/core": 1.3.78 "@swc/jest": ^0.2.26 "@types/jest": ^27.5.1 @@ -4230,6 +4229,7 @@ __metadata: prettier: ^2.7.1 prettier-plugin-packagejson: ^2.2.11 rimraf: ^4.1.2 + semver: ^7.5.4 ts-node: ^10.9.1 tsc-watch: ^4.5.0 tsup: ^8.0.1