From 7afbf126bdf078e45ee7365e368e669e6aca7f67 Mon Sep 17 00:00:00 2001 From: jdalton Date: Tue, 6 May 2025 08:59:48 -0700 Subject: [PATCH] Fix cyclical import in ts node --- .config/rollup.dist.config.mjs | 14 ++- scripts/constants.js | 4 +- src/commands/fix/npm-fix.mts | 8 +- src/commands/fix/pnpm-fix.mts | 12 +-- src/commands/raw-npm/run-raw-npm.mts | 2 +- src/commands/raw-npx/run-raw-npx.mts | 2 +- src/constants.mts | 3 - .../npm}/arborist-helpers.mts | 83 ++++++++++++++-- .../npm/arborist/lib/arborist/index.mts | 2 +- src/shadow/npm/arborist/lib/dep-valid.mts | 17 +++- src/{utils/npm.mts => shadow/npm/install.mts} | 4 +- src/shadow/npm/link.mts | 4 +- src/shadow/npm/paths.mts | 99 +------------------ src/shadow/npm/proc-log/index.mts | 2 +- src/utils/agent.mts | 2 +- src/utils/alerts-map.mts | 61 ------------ src/utils/npm-paths.mts | 99 +++++++++++++++++++ 17 files changed, 218 insertions(+), 200 deletions(-) rename src/{utils => shadow/npm}/arborist-helpers.mts (80%) rename src/{utils/npm.mts => shadow/npm/install.mts} (97%) mode change 100755 => 100644 src/shadow/npm/paths.mts create mode 100755 src/utils/npm-paths.mts diff --git a/.config/rollup.dist.config.mjs b/.config/rollup.dist.config.mjs index 313057df..a1196fc1 100644 --- a/.config/rollup.dist.config.mjs +++ b/.config/rollup.dist.config.mjs @@ -37,7 +37,6 @@ const { ROLLUP_EXTERNAL_SUFFIX, SHADOW_NPM_BIN, SHADOW_NPM_INJECT, - SHADOW_NPM_PATHS, SLASH_NODE_MODULES_SLASH, SOCKET_CLI_BIN_NAME, SOCKET_CLI_BIN_NAME_ALIAS, @@ -50,6 +49,7 @@ const { SOCKET_CLI_SENTRY_NPM_BIN_NAME, SOCKET_CLI_SENTRY_NPX_BIN_NAME, SOCKET_CLI_SENTRY_PACKAGE_NAME, + UTILS, VENDOR } = constants @@ -274,7 +274,7 @@ export default async () => { const nmPath = path.join(rootPath, NODE_MODULES) const shadowNpmBinSrcPath = path.join(srcPath, 'shadow/npm/bin.mts') const shadowNpmInjectSrcPath = path.join(srcPath, 'shadow/npm/inject.mts') - const shadowNpmPathsSrcPath = path.join(srcPath, 'shadow/npm/paths.mts') + const utilsSrcPath = path.join(srcPath, UTILS) const blessedContribFilepaths = await tinyGlob(['**/*.mjs'], { absolute: true, cwd: path.join(externalSrcPath, BLESSED_CONTRIB) @@ -362,10 +362,14 @@ export default async () => { return SHADOW_NPM_BIN case shadowNpmInjectSrcPath: return SHADOW_NPM_INJECT - case shadowNpmPathsSrcPath: - return SHADOW_NPM_PATHS default: - return id.includes(SLASH_NODE_MODULES_SLASH) ? VENDOR : null + if (id.startsWith(utilsSrcPath)) { + return UTILS + } + if (id.includes(SLASH_NODE_MODULES_SLASH)) { + return VENDOR + } + return null } }, plugins: [ diff --git a/scripts/constants.js b/scripts/constants.js index bc154b63..40524861 100644 --- a/scripts/constants.js +++ b/scripts/constants.js @@ -28,7 +28,6 @@ const INSTRUMENT_WITH_SENTRY = 'instrument-with-sentry' const ROLLUP_EXTERNAL_SUFFIX = '?commonjs-external' const SHADOW_NPM_BIN = 'shadow-bin' const SHADOW_NPM_INJECT = 'shadow-npm-inject' -const SHADOW_NPM_PATHS = 'shadow-npm-paths' const SLASH_NODE_MODULES_SLASH = '/node_modules/' const SOCKET = 'socket' const SOCKET_CLI_BIN_NAME = 'socket' @@ -42,6 +41,7 @@ const SOCKET_CLI_SENTRY_BIN_NAME = 'socket-with-sentry' const SOCKET_CLI_SENTRY_NPM_BIN_NAME = 'socket-npm-with-sentry' const SOCKET_CLI_SENTRY_NPX_BIN_NAME = 'socket-npx-with-sentry' const SOCKET_CLI_SENTRY_PACKAGE_NAME = `${SOCKET_SECURITY_SCOPE}/cli-with-sentry` +const UTILS = 'utils' const VENDOR = 'vendor' const WITH_SENTRY = 'with-sentry' @@ -105,7 +105,6 @@ const constants = createConstantsObject( ROLLUP_EXTERNAL_SUFFIX, SHADOW_NPM_BIN, SHADOW_NPM_INJECT, - SHADOW_NPM_PATHS, SLASH_NODE_MODULES_SLASH, SOCKET, SOCKET_CLI_BIN_NAME, @@ -119,6 +118,7 @@ const constants = createConstantsObject( SOCKET_CLI_SENTRY_NPM_BIN_NAME, SOCKET_CLI_SENTRY_NPX_BIN_NAME, SOCKET_CLI_SENTRY_PACKAGE_NAME, + UTILS, VENDOR, WITH_SENTRY, configPath: undefined, diff --git a/src/commands/fix/npm-fix.mts b/src/commands/fix/npm-fix.mts index a186aebf..8b707f58 100644 --- a/src/commands/fix/npm-fix.mts +++ b/src/commands/fix/npm-fix.mts @@ -31,17 +31,15 @@ import { SAFE_ARBORIST_REIFY_OPTIONS_OVERRIDES, SafeArborist } from '../../shadow/npm/arborist/lib/arborist/index.mts' -import { - getAlertsMapFromArborist, - getAlertsMapFromPurls -} from '../../utils/alerts-map.mts' import { findBestPatchVersion, findPackageNode, findPackageNodes, + getAlertsMapFromArborist, updateNode, updatePackageJsonFromNode -} from '../../utils/arborist-helpers.mts' +} from '../../shadow/npm/arborist-helpers.mts' +import { getAlertsMapFromPurls } from '../../utils/alerts-map.mts' import { removeNodeModules } from '../../utils/fs.mts' import { globWorkspace } from '../../utils/glob.mts' import { applyRange } from '../../utils/semver.mts' diff --git a/src/commands/fix/pnpm-fix.mts b/src/commands/fix/pnpm-fix.mts index 548bd675..7ed115cd 100644 --- a/src/commands/fix/pnpm-fix.mts +++ b/src/commands/fix/pnpm-fix.mts @@ -32,17 +32,17 @@ import { SAFE_ARBORIST_REIFY_OPTIONS_OVERRIDES, SafeArborist } from '../../shadow/npm/arborist/lib/arborist/index.mts' -import { runAgentInstall } from '../../utils/agent.mts' -import { - getAlertsMapFromPnpmLockfile, - getAlertsMapFromPurls -} from '../../utils/alerts-map.mts' import { findBestPatchVersion, findPackageNode, findPackageNodes, updatePackageJsonFromNode -} from '../../utils/arborist-helpers.mts' +} from '../../shadow/npm/arborist-helpers.mts' +import { runAgentInstall } from '../../utils/agent.mts' +import { + getAlertsMapFromPnpmLockfile, + getAlertsMapFromPurls +} from '../../utils/alerts-map.mts' import { removeNodeModules } from '../../utils/fs.mts' import { globWorkspace } from '../../utils/glob.mts' import { parsePnpmLockfileVersion } from '../../utils/pnpm.mts' diff --git a/src/commands/raw-npm/run-raw-npm.mts b/src/commands/raw-npm/run-raw-npm.mts index 8a3662ee..ef963575 100644 --- a/src/commands/raw-npm/run-raw-npm.mts +++ b/src/commands/raw-npm/run-raw-npm.mts @@ -1,7 +1,7 @@ import { spawn } from '@socketsecurity/registry/lib/spawn' import constants from '../../constants.mts' -import { getNpmBinPath } from '../../shadow/npm/paths.mts' +import { getNpmBinPath } from '../../utils/npm-paths.mts' export async function runRawNpm( argv: string[] | readonly string[] diff --git a/src/commands/raw-npx/run-raw-npx.mts b/src/commands/raw-npx/run-raw-npx.mts index 96f095df..0843dca2 100644 --- a/src/commands/raw-npx/run-raw-npx.mts +++ b/src/commands/raw-npx/run-raw-npx.mts @@ -1,7 +1,7 @@ import { spawn } from '@socketsecurity/registry/lib/spawn' import constants from '../../constants.mts' -import { getNpxBinPath } from '../../shadow/npm/paths.mts' +import { getNpxBinPath } from '../../utils/npm-paths.mts' export async function runRawNpx( argv: string[] | readonly string[] diff --git a/src/constants.mts b/src/constants.mts index e61bb4c7..dd1e7acb 100644 --- a/src/constants.mts +++ b/src/constants.mts @@ -108,7 +108,6 @@ type Constants = Remap< readonly REDACTED: '' readonly SHADOW_NPM_BIN: 'shadow-bin' readonly SHADOW_NPM_INJECT: 'shadow-npm-inject' - readonly SHADOW_NPM_PATHS: 'shadow-npm-paths' readonly SOCKET: 'socket' readonly SOCKET_APP_DIR: 'socket/settings' readonly SOCKET_CLI_ACCEPT_RISKS: 'SOCKET_CLI_ACCEPT_RISKS' @@ -179,7 +178,6 @@ const PNPM = 'pnpm' const REDACTED = '' const SHADOW_NPM_BIN = 'shadow-bin' const SHADOW_NPM_INJECT = 'shadow-npm-inject' -const SHADOW_NPM_PATHS = 'shadow-npm-paths' const SOCKET = 'socket' const SOCKET_APP_DIR = 'socket/settings' const SOCKET_CLI_ACCEPT_RISKS = 'SOCKET_CLI_ACCEPT_RISKS' @@ -447,7 +445,6 @@ const constants: Constants = createConstantsObject( REDACTED, SHADOW_NPM_BIN, SHADOW_NPM_INJECT, - SHADOW_NPM_PATHS, SOCKET, SOCKET_APP_DIR, SOCKET_CLI_ACCEPT_RISKS, diff --git a/src/utils/arborist-helpers.mts b/src/shadow/npm/arborist-helpers.mts similarity index 80% rename from src/utils/arborist-helpers.mts rename to src/shadow/npm/arborist-helpers.mts index 39e16dc6..2858cb80 100644 --- a/src/utils/arborist-helpers.mts +++ b/src/shadow/npm/arborist-helpers.mts @@ -5,17 +5,24 @@ import { getManifestData } from '@socketsecurity/registry' import { hasOwn } from '@socketsecurity/registry/lib/objects' import { fetchPackagePackument } from '@socketsecurity/registry/lib/packages' -import constants from '../constants.mts' -import { applyRange, getMajor } from './semver.mts' -import { idToPurl } from './spec.mts' -import { DiffAction } from '../shadow/npm/arborist/lib/arborist/types.mts' -import { Edge } from '../shadow/npm/arborist/lib/edge.mts' +import constants from '../../constants.mts' +import { applyRange, getMajor } from '../../utils/semver.mts' +import { idToPurl } from '../../utils/spec.mts' +import { DiffAction } from './arborist/lib/arborist/types.mts' +import { Edge } from './arborist/lib/edge.mts' +import { getAlertsMapFromPurls } from '../../utils/alerts-map.mts' -import type { RangeStyle } from './semver.mts' -import type { Diff } from '../shadow/npm/arborist/lib/arborist/types.mts' -import type { SafeEdge } from '../shadow/npm/arborist/lib/edge.mts' -import type { SafeNode } from '../shadow/npm/arborist/lib/node.mts' +import type { RangeStyle } from '../../utils/semver.mts' +import type { SafeArborist } from './arborist/lib/arborist/index.mts' +import type { Diff } from './arborist/lib/arborist/types.mts' +import type { SafeEdge } from './arborist/lib/edge.mts' +import type { SafeNode } from './arborist/lib/node.mts' +import type { + AlertIncludeFilter, + AlertsByPkgId +} from '../../utils/socket-package-alert.mts' import type { EditablePackageJson } from '@socketsecurity/registry/lib/packages' +import type { Spinner } from '@socketsecurity/registry/lib/spinner' const { LOOP_SENTINEL, NPM, NPM_REGISTRY_URL } = constants @@ -108,6 +115,64 @@ export function findPackageNodes( return matches } +export type GetAlertsMapFromArboristOptions = { + consolidate?: boolean | undefined + include?: AlertIncludeFilter | undefined + nothrow?: boolean | undefined + spinner?: Spinner | undefined +} + +export async function getAlertsMapFromArborist( + arb: SafeArborist, + options_?: GetAlertsMapFromArboristOptions | undefined +): Promise { + const options = { + __proto__: null, + consolidate: false, + limit: Infinity, + nothrow: false, + ...options_ + } as GetAlertsMapFromArboristOptions + + const include = { + __proto__: null, + actions: undefined, + blocked: true, + critical: true, + cve: true, + existing: false, + unfixable: true, + upgradable: false, + ...options.include + } as AlertIncludeFilter + + const needInfoOn = getDetailsFromDiff(arb.diff, { + include: { + unchanged: include.existing + } + }) + const purls = needInfoOn.map(d => idToPurl(d.node.pkgid)) + + let overrides: { [key: string]: string } | undefined + const overridesMap = ( + arb.actualTree ?? + arb.idealTree ?? + (await arb.loadActual()) + )?.overrides?.children + if (overridesMap) { + overrides = Object.fromEntries( + [...overridesMap.entries()].map(([key, overrideSet]) => { + return [key, overrideSet.value!] + }) + ) + } + + return await getAlertsMapFromPurls(purls, { + overrides, + ...options + }) +} + export type DiffQueryIncludeFilter = { unchanged?: boolean | undefined unknownOrigin?: boolean | undefined diff --git a/src/shadow/npm/arborist/lib/arborist/index.mts b/src/shadow/npm/arborist/lib/arborist/index.mts index b6868899..9ce19942 100755 --- a/src/shadow/npm/arborist/lib/arborist/index.mts +++ b/src/shadow/npm/arborist/lib/arborist/index.mts @@ -3,8 +3,8 @@ import { createRequire } from 'node:module' import { logger } from '@socketsecurity/registry/lib/logger' import constants from '../../../../../constants.mts' -import { getAlertsMapFromArborist } from '../../../../../utils/alerts-map.mts' import { logAlertsMap } from '../../../../../utils/socket-package-alert.mts' +import { getAlertsMapFromArborist } from '../../../arborist-helpers.mts' import { getArboristClassPath } from '../../../paths.mts' import type { ArboristClass, ArboristReifyOptions } from './types.mts' diff --git a/src/shadow/npm/arborist/lib/dep-valid.mts b/src/shadow/npm/arborist/lib/dep-valid.mts index 0f9ac7fb..abfbc07e 100755 --- a/src/shadow/npm/arborist/lib/dep-valid.mts +++ b/src/shadow/npm/arborist/lib/dep-valid.mts @@ -6,9 +6,22 @@ import type { SafeNode } from './node.mts' const require = createRequire(import.meta.url) -export const depValid: ( +type DepValidFn = ( child: SafeNode, requested: string, accept: string | undefined, requester: SafeNode -) => boolean = require(getArboristDepValidPath()) +) => boolean + +let _depValid: DepValidFn | undefined +export function depValid( + child: SafeNode, + requested: string, + accept: string | undefined, + requester: SafeNode +) { + if (_depValid === undefined) { + _depValid = require(getArboristDepValidPath()) as DepValidFn + } + return _depValid(child, requested, accept, requester) +} diff --git a/src/utils/npm.mts b/src/shadow/npm/install.mts similarity index 97% rename from src/utils/npm.mts rename to src/shadow/npm/install.mts index 5a00059d..19ce1777 100644 --- a/src/utils/npm.mts +++ b/src/shadow/npm/install.mts @@ -9,8 +9,8 @@ import { import { isObject } from '@socketsecurity/registry/lib/objects' import { spawn } from '@socketsecurity/registry/lib/spawn' -import constants from '../constants.mts' -import { getNpmBinPath } from '../shadow/npm/paths.mts' +import constants from '../../constants.mts' +import { getNpmBinPath } from '../../utils/npm-paths.mts' import type { Spinner } from '@socketsecurity/registry/lib/spinner' diff --git a/src/shadow/npm/link.mts b/src/shadow/npm/link.mts index 6121a9d4..cdca95be 100644 --- a/src/shadow/npm/link.mts +++ b/src/shadow/npm/link.mts @@ -2,13 +2,13 @@ import path from 'node:path' import cmdShim from 'cmd-shim' +import constants from '../../constants.mts' import { getNpmBinPath, getNpxBinPath, isNpmBinPathShadowed, isNpxBinPathShadowed -} from './paths.mts' -import constants from '../../constants.mts' +} from '../../utils/npm-paths.mts' const { CLI, NPX } = constants diff --git a/src/shadow/npm/paths.mts b/src/shadow/npm/paths.mts old mode 100755 new mode 100644 index 529e1210..b7b11e49 --- a/src/shadow/npm/paths.mts +++ b/src/shadow/npm/paths.mts @@ -1,106 +1,9 @@ -import { existsSync } from 'node:fs' -import Module from 'node:module' import path from 'node:path' -import { logger } from '@socketsecurity/registry/lib/logger' import { normalizePath } from '@socketsecurity/registry/lib/path' import constants from '../../constants.mts' -import { - findBinPathDetailsSync, - findNpmPathSync -} from '../../utils/path-resolve.mts' - -const { NODE_MODULES, NPM, NPX, SOCKET_CLI_ISSUES_URL } = constants - -function exitWithBinPathError(binName: string): never { - logger.fail( - `Socket unable to locate ${binName}; ensure it is available in the PATH environment variable` - ) - // The exit code 127 indicates that the command or binary being executed - // could not be found. - // eslint-disable-next-line n/no-process-exit - process.exit(127) -} - -let _npmBinPathDetails: ReturnType | undefined -function getNpmBinPathDetails(): ReturnType { - if (_npmBinPathDetails === undefined) { - _npmBinPathDetails = findBinPathDetailsSync(NPM) - } - return _npmBinPathDetails -} - -let _npxBinPathDetails: ReturnType | undefined -function getNpxBinPathDetails(): ReturnType { - if (_npxBinPathDetails === undefined) { - _npxBinPathDetails = findBinPathDetailsSync(NPX) - } - return _npxBinPathDetails -} - -let _npmBinPath: string | undefined -export function getNpmBinPath(): string { - if (_npmBinPath === undefined) { - _npmBinPath = getNpmBinPathDetails().path - if (!_npmBinPath) { - exitWithBinPathError(NPM) - } - } - return _npmBinPath -} - -export function isNpmBinPathShadowed() { - return getNpmBinPathDetails().shadowed -} - -let _npxBinPath: string | undefined -export function getNpxBinPath(): string { - if (_npxBinPath === undefined) { - _npxBinPath = getNpxBinPathDetails().path - if (!_npxBinPath) { - exitWithBinPathError(NPX) - } - } - return _npxBinPath -} - -export function isNpxBinPathShadowed() { - return getNpxBinPathDetails().shadowed -} - -let _npmPath: string | undefined -export function getNpmPath() { - if (_npmPath === undefined) { - const npmBinPath = getNpmBinPath() - _npmPath = npmBinPath ? findNpmPathSync(npmBinPath) : undefined - if (!_npmPath) { - let message = 'Unable to find npm CLI install directory.' - if (npmBinPath) { - message += `\nSearched parent directories of ${path.dirname(npmBinPath)}.` - } - message += `\n\nThis is may be a bug with socket-npm related to changes to the npm CLI.\nPlease report to ${SOCKET_CLI_ISSUES_URL}.` - logger.fail(message) - // The exit code 127 indicates that the command or binary being executed - // could not be found. - // eslint-disable-next-line n/no-process-exit - process.exit(127) - } - } - return _npmPath -} - -let _npmRequire: NodeJS.Require | undefined -export function getNpmRequire(): NodeJS.Require { - if (_npmRequire === undefined) { - const npmPath = getNpmPath() - const npmNmPath = path.join(npmPath, NODE_MODULES, NPM) - _npmRequire = Module.createRequire( - path.join(existsSync(npmNmPath) ? npmNmPath : npmPath, '') - ) - } - return _npmRequire -} +import { getNpmRequire } from '../../utils/npm-paths.mts' let _arboristPkgPath: string | undefined export function getArboristPackagePath() { diff --git a/src/shadow/npm/proc-log/index.mts b/src/shadow/npm/proc-log/index.mts index 98c4cb6f..d2098aaa 100755 --- a/src/shadow/npm/proc-log/index.mts +++ b/src/shadow/npm/proc-log/index.mts @@ -1,5 +1,5 @@ import constants from '../../../constants.mts' -import { getNpmRequire } from '../paths.mts' +import { getNpmRequire } from '../../../utils/npm-paths.mts' const { UNDEFINED_TOKEN } = constants diff --git a/src/utils/agent.mts b/src/utils/agent.mts index d26ce13e..0d91d23e 100644 --- a/src/utils/agent.mts +++ b/src/utils/agent.mts @@ -3,7 +3,7 @@ import { Spinner } from '@socketsecurity/registry/lib/spinner' import constants from '../constants.mts' import { cmdFlagsToString } from './cmd.mts' -import { safeNpmInstall } from './npm.mts' +import { safeNpmInstall } from '../shadow/npm/install.mts' import type { EnvDetails } from './package-environment.mts' diff --git a/src/utils/alerts-map.mts b/src/utils/alerts-map.mts index ad97c7ab..8f540f4c 100644 --- a/src/utils/alerts-map.mts +++ b/src/utils/alerts-map.mts @@ -1,78 +1,17 @@ import { arrayUnique } from '@socketsecurity/registry/lib/arrays' -import { getDetailsFromDiff } from './arborist-helpers.mts' import { extractPurlsFromPnpmLockfile } from './pnpm.mts' import { getPublicToken, setupSdk } from './sdk.mts' import { addArtifactToAlertsMap } from './socket-package-alert.mts' -import { idToPurl } from './spec.mts' import type { CompactSocketArtifact } from './alert/artifact.mts' import type { AlertIncludeFilter, AlertsByPkgId } from './socket-package-alert.mts' -import type { SafeArborist } from '../shadow/npm/arborist/lib/arborist/index.mts' import type { LockfileObject } from '@pnpm/lockfile.fs' import type { Spinner } from '@socketsecurity/registry/lib/spinner' -export type GetAlertsMapFromArboristOptions = { - consolidate?: boolean | undefined - include?: AlertIncludeFilter | undefined - nothrow?: boolean | undefined - spinner?: Spinner | undefined -} - -export async function getAlertsMapFromArborist( - arb: SafeArborist, - options_?: GetAlertsMapFromArboristOptions | undefined -): Promise { - const options = { - __proto__: null, - consolidate: false, - limit: Infinity, - nothrow: false, - ...options_ - } as GetAlertsMapFromArboristOptions - - const include = { - __proto__: null, - actions: undefined, - blocked: true, - critical: true, - cve: true, - existing: false, - unfixable: true, - upgradable: false, - ...options.include - } as AlertIncludeFilter - - const needInfoOn = getDetailsFromDiff(arb.diff, { - include: { - unchanged: include.existing - } - }) - const purls = needInfoOn.map(d => idToPurl(d.node.pkgid)) - - let overrides: { [key: string]: string } | undefined - const overridesMap = ( - arb.actualTree ?? - arb.idealTree ?? - (await arb.loadActual()) - )?.overrides?.children - if (overridesMap) { - overrides = Object.fromEntries( - [...overridesMap.entries()].map(([key, overrideSet]) => { - return [key, overrideSet.value!] - }) - ) - } - - return await getAlertsMapFromPurls(purls, { - overrides, - ...options - }) -} - export type GetAlertsMapFromPnpmLockfileOptions = { consolidate?: boolean | undefined include?: AlertIncludeFilter | undefined diff --git a/src/utils/npm-paths.mts b/src/utils/npm-paths.mts new file mode 100755 index 00000000..84c4e122 --- /dev/null +++ b/src/utils/npm-paths.mts @@ -0,0 +1,99 @@ +import { existsSync } from 'node:fs' +import Module from 'node:module' +import path from 'node:path' + +import { logger } from '@socketsecurity/registry/lib/logger' + +import constants from '../constants.mts' +import { findBinPathDetailsSync, findNpmPathSync } from './path-resolve.mts' + +const { NODE_MODULES, NPM, NPX, SOCKET_CLI_ISSUES_URL } = constants + +function exitWithBinPathError(binName: string): never { + logger.fail( + `Socket unable to locate ${binName}; ensure it is available in the PATH environment variable` + ) + // The exit code 127 indicates that the command or binary being executed + // could not be found. + // eslint-disable-next-line n/no-process-exit + process.exit(127) +} + +let _npmBinPathDetails: ReturnType | undefined +function getNpmBinPathDetails(): ReturnType { + if (_npmBinPathDetails === undefined) { + _npmBinPathDetails = findBinPathDetailsSync(NPM) + } + return _npmBinPathDetails +} + +let _npxBinPathDetails: ReturnType | undefined +function getNpxBinPathDetails(): ReturnType { + if (_npxBinPathDetails === undefined) { + _npxBinPathDetails = findBinPathDetailsSync(NPX) + } + return _npxBinPathDetails +} + +let _npmBinPath: string | undefined +export function getNpmBinPath(): string { + if (_npmBinPath === undefined) { + _npmBinPath = getNpmBinPathDetails().path + if (!_npmBinPath) { + exitWithBinPathError(NPM) + } + } + return _npmBinPath +} + +export function isNpmBinPathShadowed() { + return getNpmBinPathDetails().shadowed +} + +let _npxBinPath: string | undefined +export function getNpxBinPath(): string { + if (_npxBinPath === undefined) { + _npxBinPath = getNpxBinPathDetails().path + if (!_npxBinPath) { + exitWithBinPathError(NPX) + } + } + return _npxBinPath +} + +export function isNpxBinPathShadowed() { + return getNpxBinPathDetails().shadowed +} + +let _npmPath: string | undefined +export function getNpmPath() { + if (_npmPath === undefined) { + const npmBinPath = getNpmBinPath() + _npmPath = npmBinPath ? findNpmPathSync(npmBinPath) : undefined + if (!_npmPath) { + let message = 'Unable to find npm CLI install directory.' + if (npmBinPath) { + message += `\nSearched parent directories of ${path.dirname(npmBinPath)}.` + } + message += `\n\nThis is may be a bug with socket-npm related to changes to the npm CLI.\nPlease report to ${SOCKET_CLI_ISSUES_URL}.` + logger.fail(message) + // The exit code 127 indicates that the command or binary being executed + // could not be found. + // eslint-disable-next-line n/no-process-exit + process.exit(127) + } + } + return _npmPath +} + +let _npmRequire: NodeJS.Require | undefined +export function getNpmRequire(): NodeJS.Require { + if (_npmRequire === undefined) { + const npmPath = getNpmPath() + const npmNmPath = path.join(npmPath, NODE_MODULES, NPM) + _npmRequire = Module.createRequire( + path.join(existsSync(npmNmPath) ? npmNmPath : npmPath, '') + ) + } + return _npmRequire +}