diff --git a/.changeset/gorgeous-tips-walk.md b/.changeset/gorgeous-tips-walk.md new file mode 100644 index 00000000000..aaf5d8d2bd8 --- /dev/null +++ b/.changeset/gorgeous-tips-walk.md @@ -0,0 +1,7 @@ +--- +"create-fuels": patch +"fuels": patch +"@fuel-ts/utils": patch +--- + +Fixing and internalizing `findBinPath` utility diff --git a/packages/fuels/package.json b/packages/fuels/package.json index ca8383d9c00..ef401f4e7b9 100644 --- a/packages/fuels/package.json +++ b/packages/fuels/package.json @@ -76,7 +76,6 @@ "handlebars": "^4.7.7", "joycon": "^3.1.1", "lodash.camelcase": "^4.3.0", - "npm-which": "^3.0.1", "portfinder": "^1.0.32", "rimraf": "^3.0.2", "toml": "^3.0.0", diff --git a/packages/fuels/src/cli.ts b/packages/fuels/src/cli.ts index 342fc4bd348..427fbac360f 100644 --- a/packages/fuels/src/cli.ts +++ b/packages/fuels/src/cli.ts @@ -1,7 +1,7 @@ import { configureCliOptions as configureTypegenCliOptions } from '@fuel-ts/abi-typegen/cli'; +import { findBinPath } from '@fuel-ts/utils/cli-utils'; import { versions } from '@fuel-ts/versions'; import { runVersions } from '@fuel-ts/versions/cli'; -import { findBinPath } from '@fuel-ts/wallet/test-utils'; import { Command, Option } from 'commander'; import { build } from './cli/commands/build'; @@ -100,11 +100,11 @@ export const configureCli = () => { */ program.command('core', 'Wrapper around Fuel Core binary', { - executableFile: findBinPath('fuels-core'), + executableFile: findBinPath('fuels-core', __dirname), }); program.command('forc', 'Wrapper around Forc binary', { - executableFile: findBinPath('fuels-forc'), + executableFile: findBinPath('fuels-forc', __dirname), }); return program; diff --git a/packages/fuels/src/cli/commands/build/buildSwayProgram.test.ts b/packages/fuels/src/cli/commands/build/buildSwayProgram.test.ts index a10c39d675a..24cf1be223b 100644 --- a/packages/fuels/src/cli/commands/build/buildSwayProgram.test.ts +++ b/packages/fuels/src/cli/commands/build/buildSwayProgram.test.ts @@ -1,4 +1,4 @@ -import * as findBinPathMod from '@fuel-ts/wallet/test-utils'; +import * as findBinPathMod from '@fuel-ts/utils/cli-utils'; import * as childProcessMod from 'child_process'; import { fuelsConfig } from '../../../../test/fixtures/fuels.config'; diff --git a/packages/fuels/src/cli/commands/build/buildSwayProgram.ts b/packages/fuels/src/cli/commands/build/buildSwayProgram.ts index 9c09af10a5f..80780a6984b 100644 --- a/packages/fuels/src/cli/commands/build/buildSwayProgram.ts +++ b/packages/fuels/src/cli/commands/build/buildSwayProgram.ts @@ -1,4 +1,4 @@ -import { findBinPath } from '@fuel-ts/wallet/test-utils'; +import { findBinPath } from '@fuel-ts/utils/cli-utils'; import { spawn } from 'child_process'; import type { FuelsConfig } from '../../types'; @@ -10,7 +10,7 @@ export const buildSwayProgram = async (config: FuelsConfig, path: string) => { debug('Building Sway program', path); return new Promise((resolve, reject) => { - const builtInForcPath = findBinPath('fuels-forc'); + const builtInForcPath = findBinPath('fuels-forc', __dirname); const command = config.useBuiltinForc ? builtInForcPath : 'forc'; const forc = spawn(command, ['build', '-p', path], { stdio: 'pipe' }); diff --git a/packages/utils/package.json b/packages/utils/package.json index 7102614d58d..482fc781e1f 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -19,12 +19,20 @@ "require": "./dist/test-utils.js", "import": "./dist/test-utils.mjs", "types": "./dist/test-utils.d.ts" + }, + "./cli-utils": { + "require": "./dist/cli-utils.js", + "import": "./dist/cli-utils.mjs", + "types": "./dist/cli-utils.d.ts" } }, "typesVersions": { "*": { "test-utils": [ "./dist/test-utils.d.ts" + ], + "cli-utils": [ + "./dist/cli-utils.d.ts" ] } }, diff --git a/packages/utils/src/cli-utils.ts b/packages/utils/src/cli-utils.ts new file mode 100644 index 00000000000..eaac016729a --- /dev/null +++ b/packages/utils/src/cli-utils.ts @@ -0,0 +1 @@ +export * from './cli-utils/findBinPath'; diff --git a/packages/utils/src/cli-utils/findBinPath.test.ts b/packages/utils/src/cli-utils/findBinPath.test.ts new file mode 100644 index 00000000000..cb1ddde4c9c --- /dev/null +++ b/packages/utils/src/cli-utils/findBinPath.test.ts @@ -0,0 +1,57 @@ +import { safeExec } from '@fuel-ts/errors/test-utils'; +import { mkdirSync, rmSync, writeFileSync } from 'fs'; +import { join } from 'path'; + +import { findBinPath } from './findBinPath'; + +/** + * @group node + */ +describe('findBinPath', () => { + const bootstrap = (dir: string) => { + const cmdName = 'my-cmd'; + const mods = join(dir, 'node_modules'); + const bin = join(mods, '.bin'); + const cmdPath = join(bin, cmdName); + + const resetDisk = () => rmSync(mods, { recursive: true }); + + mkdirSync(bin, { recursive: true }); + writeFileSync(cmdPath, ''); + + return { resetDisk, mods, cmdName, cmdPath }; + }; + + it('should find bin path in current dir', () => { + const base = __dirname; // current dir + const { cmdName, cmdPath, resetDisk } = bootstrap(base); + const binPath = findBinPath(cmdName, base); + + resetDisk(); + expect(binPath).toEqual(cmdPath); + }); + + it('should find bin path one dir up', () => { + const base = join(__dirname, '..'); // one dir up + const { cmdName, cmdPath, resetDisk } = bootstrap(base); + const binPath = findBinPath(cmdName, base); + + resetDisk(); + expect(binPath).toEqual(cmdPath); + }); + + it('should find bin path two dir up', () => { + const base = join(__dirname, '..', '..'); // two dirs up + const { cmdName, cmdPath, resetDisk } = bootstrap(base); + const binPath = findBinPath(cmdName, base); + + resetDisk(); + expect(binPath).toEqual(cmdPath); + }); + + it('should throw for bin path not found', async () => { + const cmdName = 'non-existent'; + const { error } = await safeExec(() => findBinPath(cmdName, __dirname)); + expect(error?.message).toEqual(`Command not found: ${cmdName}`); + }); +}); diff --git a/packages/utils/src/cli-utils/findBinPath.ts b/packages/utils/src/cli-utils/findBinPath.ts new file mode 100644 index 00000000000..7f255e01bd1 --- /dev/null +++ b/packages/utils/src/cli-utils/findBinPath.ts @@ -0,0 +1,17 @@ +import { existsSync } from 'fs'; +import { join } from 'path'; + +export const findBinPath = (binCommandName: string, startingDir: string): string => { + const cmdPath = join(startingDir, 'node_modules', '.bin', binCommandName); + const parentDir = join(startingDir, '..'); + + if (existsSync(cmdPath)) { + return cmdPath; + } + + if (parentDir === startingDir) { + throw new Error(`Command not found: ${binCommandName}`); + } + + return findBinPath(binCommandName, parentDir); +}; diff --git a/packages/utils/tsup.config.ts b/packages/utils/tsup.config.ts index 4972257d818..ce3ba9f9aeb 100644 --- a/packages/utils/tsup.config.ts +++ b/packages/utils/tsup.config.ts @@ -6,6 +6,7 @@ const configs: Options = { entry: { index: 'src/index.ts', 'test-utils': 'src/test-utils.ts', + 'cli-utils': 'src/cli-utils.ts', }, }; diff --git a/packages/wallet/package.json b/packages/wallet/package.json index 63b2ce34343..70153c6cc1b 100644 --- a/packages/wallet/package.json +++ b/packages/wallet/package.json @@ -61,7 +61,6 @@ "@fuel-ts/utils": "workspace:*", "@fuels/vm-asm": "0.42.1", "ethers": "^6.7.1", - "npm-which": "^3.0.1", "portfinder": "^1.0.32", "tree-kill": "^1.2.2", "uuid": "^9.0.0" diff --git a/packages/wallet/src/test-utils/launchNode.ts b/packages/wallet/src/test-utils/launchNode.ts index 67552a1b8bf..cfbd1f4684d 100644 --- a/packages/wallet/src/test-utils/launchNode.ts +++ b/packages/wallet/src/test-utils/launchNode.ts @@ -3,6 +3,7 @@ import { toHex } from '@fuel-ts/math'; import { Provider } from '@fuel-ts/providers'; import { Signer } from '@fuel-ts/signer'; import { defaultChainConfig, defaultConsensusKey } from '@fuel-ts/utils'; +import { findBinPath } from '@fuel-ts/utils/cli-utils'; import type { ChildProcessWithoutNullStreams } from 'child_process'; import { spawn } from 'child_process'; import { randomUUID } from 'crypto'; @@ -17,9 +18,6 @@ import type { WalletUnlocked } from '../wallets'; import { generateTestWallet } from './generateTestWallet'; -// eslint-disable-next-line @typescript-eslint/no-var-requires -const npmWhich = require('npm-which')(__dirname); - const getFlagValueFromArgs = (args: string[], flag: string) => { const flagIndex = args.indexOf(flag); if (flagIndex === -1) { @@ -65,17 +63,6 @@ export type KillNodeParams = { }; }; -export const findBinPath = (binCommandName: string) => { - let binPath = npmWhich.sync(binCommandName); - - if (!existsSync(binPath)) { - // The user might be using bun, which has a different structure for binaries inside node_modules - binPath = path.join('node_modules', '.bin', binCommandName); - } - - return binPath; -}; - export const killNode = (params: KillNodeParams) => { const { child, configPath, state, killFn } = params; if (!state.isDead) { @@ -138,7 +125,7 @@ export const launchNode = async ({ // This string is logged by the client when the node has successfully started. We use it to know when to resolve. const graphQLStartSubstring = 'Binding GraphQL provider to'; - const binPath = await findBinPath('fuels-core'); + const binPath = findBinPath('fuels-core', __dirname); const command = useSystemFuelCore ? 'fuel-core' : binPath; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d729dd1e54d..ccaf958ca6d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -784,9 +784,6 @@ importers: lodash.camelcase: specifier: ^4.3.0 version: 4.3.0 - npm-which: - specifier: ^3.0.1 - version: 3.0.1 portfinder: specifier: ^1.0.32 version: 1.0.32 @@ -811,7 +808,7 @@ importers: version: 3.0.2 vite: specifier: ^4.3.9 - version: 4.3.9(@types/node@18.15.3) + version: 4.3.9 packages/hasher: dependencies: @@ -1217,9 +1214,6 @@ importers: ethers: specifier: ^6.7.1 version: 6.7.1 - npm-which: - specifier: ^3.0.1 - version: 3.0.1 portfinder: specifier: ^1.0.32 version: 1.0.32 @@ -12499,6 +12493,17 @@ packages: ms: 2.0.0 dev: false + /debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + dev: false + /debug@3.2.7(supports-color@5.5.0): resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -18375,14 +18380,6 @@ packages: engines: {node: '>=14.16'} dev: true - /npm-path@2.0.4: - resolution: {integrity: sha512-IFsj0R9C7ZdR5cP+ET342q77uSRdtWOlWpih5eC+lu29tIDbNEgDbzgVJ5UFvYHWhxDZ5TFkJafFioO0pPQjCw==} - engines: {node: '>=0.8'} - hasBin: true - dependencies: - which: 1.3.1 - dev: false - /npm-run-all@4.1.5: resolution: {integrity: sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==} engines: {node: '>= 4'} @@ -18412,16 +18409,6 @@ packages: path-key: 4.0.0 dev: true - /npm-which@3.0.1: - resolution: {integrity: sha512-CM8vMpeFQ7MAPin0U3wzDhSGV0hMHNwHU0wjo402IVizPDrs45jSfSuoC+wThevY88LQti8VvaAnqYAeVy3I1A==} - engines: {node: '>=4.2.0'} - hasBin: true - dependencies: - commander: 2.20.3 - npm-path: 2.0.4 - which: 1.3.1 - dev: false - /nth-check@1.0.2: resolution: {integrity: sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==} dependencies: @@ -19140,7 +19127,7 @@ packages: engines: {node: '>= 0.12.0'} dependencies: async: 2.6.4 - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7 mkdirp: 0.5.6 transitivePeerDependencies: - supports-color @@ -23361,6 +23348,38 @@ packages: minimatch: 6.2.0 dev: true + /vite@4.3.9: + resolution: {integrity: sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + esbuild: 0.17.19 + postcss: 8.4.24 + rollup: 3.25.3 + optionalDependencies: + fsevents: 2.3.3 + dev: true + /vite@4.3.9(@types/node@18.15.3): resolution: {integrity: sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==} engines: {node: ^14.18.0 || >=16.0.0}