diff --git a/.changeset/stop-using-npm-config-for-auth.md b/.changeset/stop-using-npm-config-for-auth.md new file mode 100644 index 0000000000..f1bea0b303 --- /dev/null +++ b/.changeset/stop-using-npm-config-for-auth.md @@ -0,0 +1,9 @@ +--- +"@pnpm/config.commands": minor +"@pnpm/config.reader": patch +"pnpm": minor +--- + +`pnpm config get/set/delete` no longer shells out to `npm config` for auth-related settings. Auth settings (registry, tokens, credentials, scoped registries) are now read from and written to the INI config files directly. + +Auth settings from the pnpm global rc file (`~/.config/pnpm/rc`) now take priority over `~/.npmrc`, so tokens written by `pnpm login` are correctly picked up by `pnpm publish`. diff --git a/config/commands/package.json b/config/commands/package.json index cd2fb87448..cef2c0e10d 100644 --- a/config/commands/package.json +++ b/config/commands/package.json @@ -36,7 +36,6 @@ "@pnpm/config.reader": "workspace:*", "@pnpm/constants": "workspace:*", "@pnpm/error": "workspace:*", - "@pnpm/exec.run-npm": "workspace:*", "@pnpm/object.key-sorting": "workspace:*", "@pnpm/object.property-path": "workspace:*", "@pnpm/text.naming-cases": "workspace:*", diff --git a/config/commands/src/ConfigCommandOptions.ts b/config/commands/src/ConfigCommandOptions.ts index 6bd43c007d..725caba879 100644 --- a/config/commands/src/ConfigCommandOptions.ts +++ b/config/commands/src/ConfigCommandOptions.ts @@ -5,7 +5,6 @@ export type ConfigCommandOptions = Pick & { diff --git a/config/commands/src/configGet.ts b/config/commands/src/configGet.ts index 548b767d60..32c6ea3b7c 100644 --- a/config/commands/src/configGet.ts +++ b/config/commands/src/configGet.ts @@ -1,7 +1,4 @@ -import path from 'node:path' - -import { types } from '@pnpm/config.reader' -import { runNpm } from '@pnpm/exec.run-npm' +import { isIniConfigKey, types } from '@pnpm/config.reader' import { getObjectValueByPropertyPath } from '@pnpm/object.property-path' import { isCamelCase, isStrictlyKebabCase } from '@pnpm/text.naming-cases' import kebabCase from 'lodash.kebabcase' @@ -9,19 +6,9 @@ import kebabCase from 'lodash.kebabcase' import type { ConfigCommandOptions } from './ConfigCommandOptions.js' import { parseConfigPropertyPath } from './parseConfigPropertyPath.js' import { processConfig } from './processConfig.js' -import { settingShouldFallBackToNpm } from './settingShouldFallBackToNpm.js' export function configGet (opts: ConfigCommandOptions, key: string): { output: string, exitCode: number } { const isScopedKey = key.startsWith('@') - // Exclude scoped keys from npm fallback because they are pnpm-native config - // that can be read directly from rawConfig (e.g., '@scope:registry') - if (opts.global && settingShouldFallBackToNpm(key) && !isScopedKey) { - const { status: exitCode } = runNpm(opts.npmPath, ['config', 'get', key], { - location: 'user', - userConfigPath: path.join(opts.configDir, 'rc'), - }) - return { output: '', exitCode: exitCode ?? 0 } - } const configResult = getRcConfig(opts.rawConfig, key, isScopedKey) ?? getConfigByPropertyPath(opts.rawConfig, key) const output = displayConfig(configResult?.value, opts) return { output, exitCode: 0 } @@ -45,6 +32,10 @@ function getRcConfig (rawConfig: Record, key: string, isScopedK const value = rawConfig[key] return { value } } + if (isIniConfigKey(key)) { + const value = rawConfig[key] + return { value } + } return undefined } diff --git a/config/commands/src/configSet.ts b/config/commands/src/configSet.ts index b19c1ad066..938204c71f 100644 --- a/config/commands/src/configSet.ts +++ b/config/commands/src/configSet.ts @@ -1,10 +1,9 @@ import path from 'node:path' import util from 'node:util' -import { type ConfigFileKey, isConfigFileKey, types } from '@pnpm/config.reader' +import { type ConfigFileKey, isConfigFileKey, isIniConfigKey, types } from '@pnpm/config.reader' import { GLOBAL_CONFIG_YAML_FILENAME, WORKSPACE_MANIFEST_FILENAME } from '@pnpm/constants' import { PnpmError } from '@pnpm/error' -import { runNpm, type RunNPMOptions } from '@pnpm/exec.run-npm' import { parsePropertyPath } from '@pnpm/object.property-path' import { isCamelCase, isStrictlyKebabCase } from '@pnpm/text.naming-cases' import { updateWorkspaceManifest } from '@pnpm/workspace.workspace-manifest-writer' @@ -15,48 +14,34 @@ import { writeIniFile } from 'write-ini-file' import type { ConfigCommandOptions } from './ConfigCommandOptions.js' import { getConfigFileInfo } from './getConfigFileInfo.js' -import { settingShouldFallBackToNpm } from './settingShouldFallBackToNpm.js' export async function configSet (opts: ConfigCommandOptions, key: string, valueParam: string | null): Promise { - let shouldFallbackToNpm = settingShouldFallBackToNpm(key) - if (!shouldFallbackToNpm) { + let isAuthSetting = isIniConfigKey(key) + if (!isAuthSetting) { key = validateSimpleKey(key) - shouldFallbackToNpm = settingShouldFallBackToNpm(key) + isAuthSetting = isIniConfigKey(key) } let value: unknown = valueParam if (valueParam != null && opts.json) { value = JSON.parse(valueParam) } - if (shouldFallbackToNpm) { - if (opts.global) { - const configPath = path.join(opts.configDir, 'rc') - const runNpmOpts: RunNPMOptions = { - location: 'user', - userConfigPath: configPath, - } - const _runNpm = runNpm.bind(null, opts.npmPath) - if (value == null) { - _runNpm(['config', 'delete', key], runNpmOpts) - return - } - if (typeof value === 'string') { - _runNpm(['config', 'set', `${key}=${value}`], runNpmOpts) - return - } + if (isAuthSetting) { + const configPath = opts.global + ? path.join(opts.configDir, 'rc') + : path.join(opts.dir, '.npmrc') + if (value != null && typeof value !== 'string' && isStringOnlyIniKey(key)) { throw new PnpmError('CONFIG_SET_AUTH_NON_STRING', `Cannot set ${key} to a non-string value (${JSON.stringify(value)})`) - } else { - const configPath = path.join(opts.dir, '.npmrc') - const settings = await safeReadIniFile(configPath) - if (value == null) { - if (settings[key] == null) return - delete settings[key] - } else { - settings[key] = value - } - await writeIniFile(configPath, settings) - return } + const settings = await safeReadIniFile(configPath) + if (value == null) { + if (settings[key] == null) return + delete settings[key] + } else { + settings[key] = value + } + await writeIniFile(configPath, settings) + return } const { configDir, configFileName } = getConfigFileInfo(key, opts) @@ -214,6 +199,15 @@ function validateWorkspaceKey (key: string): string { return key } +const STRING_ONLY_INI_KEYS = ['_auth', '_authToken', '_password', 'username', 'registry'] + +function isStringOnlyIniKey (key: string): boolean { + if (STRING_ONLY_INI_KEYS.includes(key)) return true + if (key.startsWith('@')) return true + if (key.startsWith('//')) return true + return false +} + async function safeReadIniFile (configPath: string): Promise> { try { return await readIniFile(configPath) as Record diff --git a/config/commands/src/settingShouldFallBackToNpm.ts b/config/commands/src/settingShouldFallBackToNpm.ts deleted file mode 100644 index f545fe0d26..0000000000 --- a/config/commands/src/settingShouldFallBackToNpm.ts +++ /dev/null @@ -1,10 +0,0 @@ -// NOTE: The logic may be duplicated with `isIniConfigKey` from `@pnpm/config.reader`, -// but we have not the time to refactor it right now. -// TODO: Refactor it when we have the time. -export function settingShouldFallBackToNpm (key: string): boolean { - return ( - ['registry', '_auth', '_authToken', 'username', '_password'].includes(key) || - key[0] === '@' || - key.startsWith('//') - ) -} diff --git a/config/commands/test/configDelete.test.ts b/config/commands/test/configDelete.test.ts index 2135501e67..82952e155a 100644 --- a/config/commands/test/configDelete.test.ts +++ b/config/commands/test/configDelete.test.ts @@ -40,7 +40,7 @@ test('config delete on registry key set', async () => { rawConfig: {}, }, ['delete', 'registry']) - expect(fs.readdirSync(configDir)).not.toContain('rc') + expect(readIniFileSync(path.join(configDir, 'rc'))).toEqual({}) }) test('config delete on npm-compatible key not set', async () => { diff --git a/config/commands/test/managingAuthSettings.test.ts b/config/commands/test/managingAuthSettings.test.ts index 207d4a13b4..ab593fd37c 100644 --- a/config/commands/test/managingAuthSettings.test.ts +++ b/config/commands/test/managingAuthSettings.test.ts @@ -1,11 +1,9 @@ -import { jest } from '@jest/globals' +import path from 'node:path' -jest.unstable_mockModule('@pnpm/exec.run-npm', () => ({ - runNpm: jest.fn(), -})) +import { config } from '@pnpm/config.commands' +import { tempDir } from '@pnpm/prepare' -const { config } = await import('@pnpm/config.commands') -const { runNpm } = await import('@pnpm/exec.run-npm') +import { type ConfigFilesData, readConfigFiles, writeConfigFiles } from './utils/index.js' describe.each( [ @@ -14,54 +12,165 @@ describe.each( '_password', 'username', 'registry', - '@foo:registry', '//registry.npmjs.org/:_authToken', ] -)('settings related to auth are handled by npm CLI', (key) => { - describe('without --json', () => { - const configOpts = { - dir: process.cwd(), - cliOptions: {}, - configDir: import.meta.dirname, // this doesn't matter, it won't be used - rawConfig: {}, - } +)('auth settings are written to the rc file directly', (key) => { + describe('global (without --json)', () => { it(`should set ${key}`, async () => { - await config.handler(configOpts, ['set', `${key}=123`]) - expect(runNpm).toHaveBeenCalledWith(undefined, ['config', 'set', `${key}=123`], expect.objectContaining({ - location: 'user', - userConfigPath: expect.any(String), - })) + const tmp = tempDir() + const configDir = path.join(tmp, 'global-config') + const initConfig = { + globalRc: {}, + globalYaml: undefined, + localRc: undefined, + localYaml: undefined, + } satisfies ConfigFilesData + writeConfigFiles(configDir, tmp, initConfig) + + await config.handler({ + dir: tmp, + cliOptions: {}, + configDir, + global: true, + rawConfig: {}, + }, ['set', `${key}=123`]) + + expect(readConfigFiles(configDir, tmp)).toEqual({ + ...initConfig, + globalRc: { [key]: '123' }, + }) }) it(`should delete ${key}`, async () => { - await config.handler(configOpts, ['delete', key]) - expect(runNpm).toHaveBeenCalledWith(undefined, ['config', 'delete', key], expect.objectContaining({ - location: 'user', - userConfigPath: expect.any(String), - })) + const tmp = tempDir() + const configDir = path.join(tmp, 'global-config') + const initConfig = { + globalRc: { [key]: 'some-value' }, + globalYaml: undefined, + localRc: undefined, + localYaml: undefined, + } satisfies ConfigFilesData + writeConfigFiles(configDir, tmp, initConfig) + + await config.handler({ + dir: tmp, + cliOptions: {}, + configDir, + global: true, + rawConfig: {}, + }, ['delete', key]) + + expect(readConfigFiles(configDir, tmp)).toEqual({ + ...initConfig, + globalRc: {}, + }) }) }) - describe('with --json', () => { - const configOpts = { - json: true, - dir: process.cwd(), - cliOptions: {}, - configDir: import.meta.dirname, // this doesn't matter, it won't be used - rawConfig: {}, - } + describe('global (with --json)', () => { it(`should set ${key}`, async () => { - await config.handler(configOpts, ['set', key, '"123"']) - expect(runNpm).toHaveBeenCalledWith(undefined, ['config', 'set', `${key}=123`], expect.objectContaining({ - location: 'user', - userConfigPath: expect.any(String), - })) + const tmp = tempDir() + const configDir = path.join(tmp, 'global-config') + const initConfig = { + globalRc: {}, + globalYaml: undefined, + localRc: undefined, + localYaml: undefined, + } satisfies ConfigFilesData + writeConfigFiles(configDir, tmp, initConfig) + + await config.handler({ + json: true, + dir: tmp, + cliOptions: {}, + configDir, + global: true, + rawConfig: {}, + }, ['set', key, '"123"']) + + expect(readConfigFiles(configDir, tmp)).toEqual({ + ...initConfig, + globalRc: { [key]: '123' }, + }) }) it(`should delete ${key}`, async () => { - await config.handler(configOpts, ['delete', key]) - expect(runNpm).toHaveBeenCalledWith(undefined, ['config', 'delete', key], expect.objectContaining({ - location: 'user', - userConfigPath: expect.any(String), - })) + const tmp = tempDir() + const configDir = path.join(tmp, 'global-config') + const initConfig = { + globalRc: { [key]: 'some-value' }, + globalYaml: undefined, + localRc: undefined, + localYaml: undefined, + } satisfies ConfigFilesData + writeConfigFiles(configDir, tmp, initConfig) + + await config.handler({ + json: true, + dir: tmp, + cliOptions: {}, + configDir, + global: true, + rawConfig: {}, + }, ['delete', key]) + + expect(readConfigFiles(configDir, tmp)).toEqual({ + ...initConfig, + globalRc: {}, + }) + }) + }) +}) + +describe.each( + [ + '@foo:registry', + ] +)('scoped auth settings are written to the rc file directly', (key) => { + it(`should set ${key} globally`, async () => { + const tmp = tempDir() + const configDir = path.join(tmp, 'global-config') + const initConfig = { + globalRc: {}, + globalYaml: undefined, + localRc: undefined, + localYaml: undefined, + } satisfies ConfigFilesData + writeConfigFiles(configDir, tmp, initConfig) + + await config.handler({ + dir: tmp, + cliOptions: {}, + configDir, + global: true, + rawConfig: {}, + }, ['set', `${key}=https://registry.example.com/`]) + + expect(readConfigFiles(configDir, tmp)).toEqual({ + ...initConfig, + globalRc: { [key]: 'https://registry.example.com/' }, + }) + }) + it(`should delete ${key} globally`, async () => { + const tmp = tempDir() + const configDir = path.join(tmp, 'global-config') + const initConfig = { + globalRc: { [key]: 'https://registry.example.com/' }, + globalYaml: undefined, + localRc: undefined, + localYaml: undefined, + } satisfies ConfigFilesData + writeConfigFiles(configDir, tmp, initConfig) + + await config.handler({ + dir: tmp, + cliOptions: {}, + configDir, + global: true, + rawConfig: {}, + }, ['delete', key]) + + expect(readConfigFiles(configDir, tmp)).toEqual({ + ...initConfig, + globalRc: {}, }) }) }) @@ -77,15 +186,17 @@ describe.each( '//registry.npmjs.org/:_authToken', ] )('non-string values should be rejected', (key) => { - const configOpts = { - json: true, - dir: process.cwd(), - cliOptions: {}, - configDir: import.meta.dirname, // this doesn't matter, it won't be used - rawConfig: {}, - } + const tmp = tempDir() + const configDir = path.join(tmp, 'global-config') it(`${key} should reject a non-string value`, async () => { - await expect(config.handler(configOpts, ['set', key, '{}'])).rejects.toMatchObject({ + await expect(config.handler({ + json: true, + dir: tmp, + cliOptions: {}, + configDir, + global: true, + rawConfig: {}, + }, ['set', key, '{}'])).rejects.toMatchObject({ code: 'ERR_PNPM_CONFIG_SET_AUTH_NON_STRING', }) }) @@ -96,25 +207,53 @@ describe.each( '._auth', "['_auth']", ] -)('%p is handled by npm CLI', (propertyPath) => { - const configOpts = { - dir: process.cwd(), - cliOptions: {}, - configDir: import.meta.dirname, // this doesn't matter, it won't be used - rawConfig: {}, - } +)('%p is handled as an auth setting', (propertyPath) => { it('should set _auth', async () => { - await config.handler(configOpts, ['set', propertyPath, '123']) - expect(runNpm).toHaveBeenCalledWith(undefined, ['config', 'set', '_auth=123'], expect.objectContaining({ - location: 'user', - userConfigPath: expect.any(String), - })) + const tmp = tempDir() + const configDir = path.join(tmp, 'global-config') + const initConfig = { + globalRc: {}, + globalYaml: undefined, + localRc: undefined, + localYaml: undefined, + } satisfies ConfigFilesData + writeConfigFiles(configDir, tmp, initConfig) + + await config.handler({ + dir: tmp, + cliOptions: {}, + configDir, + global: true, + rawConfig: {}, + }, ['set', propertyPath, '123']) + + expect(readConfigFiles(configDir, tmp)).toEqual({ + ...initConfig, + globalRc: { _auth: '123' }, + }) }) it('should delete _auth', async () => { - await config.handler(configOpts, ['delete', propertyPath]) - expect(runNpm).toHaveBeenCalledWith(undefined, ['config', 'delete', '_auth'], expect.objectContaining({ - location: 'user', - userConfigPath: expect.any(String), - })) + const tmp = tempDir() + const configDir = path.join(tmp, 'global-config') + const initConfig = { + globalRc: { _auth: 'some-value' }, + globalYaml: undefined, + localRc: undefined, + localYaml: undefined, + } satisfies ConfigFilesData + writeConfigFiles(configDir, tmp, initConfig) + + await config.handler({ + dir: tmp, + cliOptions: {}, + configDir, + global: true, + rawConfig: {}, + }, ['delete', propertyPath]) + + expect(readConfigFiles(configDir, tmp)).toEqual({ + ...initConfig, + globalRc: {}, + }) }) }) diff --git a/config/commands/tsconfig.json b/config/commands/tsconfig.json index 95d777aea0..c986b3de99 100644 --- a/config/commands/tsconfig.json +++ b/config/commands/tsconfig.json @@ -24,9 +24,6 @@ { "path": "../../core/logger" }, - { - "path": "../../exec/run-npm" - }, { "path": "../../object/key-sorting" }, diff --git a/config/reader/src/index.ts b/config/reader/src/index.ts index 7e098a7c45..44f3ffc08e 100644 --- a/config/reader/src/index.ts +++ b/config/reader/src/index.ts @@ -294,9 +294,14 @@ export async function getConfig (opts: { pnpmConfig.userAgent = pnpmConfig.rawLocalConfig['user-agent'] ? pnpmConfig.rawLocalConfig['user-agent'] : `${packageManager.name}/${packageManager.version} npm/? node/${process.version} ${process.platform} ${process.arch}` + const pnpmGlobalRcData = npmConfig.sources['pnpm-global']?.data + const pnpmGlobalRc = pnpmGlobalRcData + ? pickIniConfig(Object.fromEntries(Object.entries(pnpmGlobalRcData))) + : {} pnpmConfig.rawConfig = Object.assign( {}, ...npmConfig.list.map(pickIniConfig).reverse(), + pnpmGlobalRc, pickIniConfig(cliOptions), { 'user-agent': pnpmConfig.userAgent }, { globalconfig: path.join(configDir, 'rc') }, diff --git a/config/reader/test/index.ts b/config/reader/test/index.ts index 6ea942e283..9872d06610 100644 --- a/config/reader/test/index.ts +++ b/config/reader/test/index.ts @@ -444,6 +444,47 @@ test('registries in current directory\'s .npmrc have bigger priority then global }) }) +test('auth tokens from pnpm global rc override ~/.npmrc', async () => { + prepareEmpty() + + // Set up a userconfig (.npmrc) with a stale token + fs.writeFileSync('.npmrc', '//registry.npmjs.org/:_authToken=stale-token', 'utf8') + + // Set up a pnpm global rc with a fresh token via XDG_CONFIG_HOME + const configHome = path.resolve('xdg-config') + fs.mkdirSync(path.join(configHome, 'pnpm'), { recursive: true }) + fs.writeFileSync( + path.join(configHome, 'pnpm', 'rc'), + '//registry.npmjs.org/:_authToken=fresh-token' + ) + + const originalXdg = process.env.XDG_CONFIG_HOME + process.env.XDG_CONFIG_HOME = configHome + try { + const { config } = await getConfig({ + cliOptions: { + userconfig: path.resolve('.npmrc'), + }, + env: { + ...env, + XDG_CONFIG_HOME: configHome, + }, + packageManager: { + name: 'pnpm', + version: '1.0.0', + }, + }) + + expect(config.rawConfig['//registry.npmjs.org/:_authToken']).toBe('fresh-token') + } finally { + if (originalXdg != null) { + process.env.XDG_CONFIG_HOME = originalXdg + } else { + delete process.env.XDG_CONFIG_HOME + } + } +}) + test('throw error if --save-prod is used with --save-peer', async () => { await expect(getConfig({ cliOptions: { diff --git a/exec/run-npm/CHANGELOG.md b/exec/run-npm/CHANGELOG.md deleted file mode 100644 index 32381f4fca..0000000000 --- a/exec/run-npm/CHANGELOG.md +++ /dev/null @@ -1,68 +0,0 @@ -# @pnpm/run-npm - -## 7.0.0 - -### Major Changes - -- 43cdd87: Node.js v16 support dropped. Use at least Node.js v18.12. - -### Patch Changes - -- e70a829: The npm CLI executed from pnpm should not use Corepack [#7747](https://github.com/pnpm/pnpm/pull/7747). - -## 6.1.0 - -### Minor Changes - -- 5a5e42551: Allow to set custom env. -- 5a5e42551: Allow using token helpers in `pnpm publish` [#7316](https://github.com/pnpm/pnpm/issues/7316). - -## 6.0.0 - -### Major Changes - -- eceaa8b8b: Node.js 14 support dropped. - -## 5.0.0 - -### Major Changes - -- f884689e0: Require `@pnpm/logger` v5. - -## 4.0.1 - -### Patch Changes - -- 9177ddbc9: If running npm errors, throw an error. - -## 4.0.0 - -### Major Changes - -- 542014839: Node.js 12 is not supported. - -## 3.1.0 - -### Minor Changes - -- 724c5abd8: support "publishConfig.directory" field - -## 3.0.0 - -### Major Changes - -- 97b986fbc: Node.js 10 support is dropped. At least Node.js 12.17 is required for the package to work. - -## 2.0.3 - -### Patch Changes - -- a2ef8084f: Use the same versions of dependencies across the pnpm monorepo. - -## 2.0.2 - -### Patch Changes - -- c56438567: Update cross-spawn to ^7.0.3 - -## 2.0.2 diff --git a/exec/run-npm/README.md b/exec/run-npm/README.md deleted file mode 100644 index 905ceb117e..0000000000 --- a/exec/run-npm/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# @pnpm/run-npm - -> Runs the npm CLI - -[![npm version](https://img.shields.io/npm/v/@pnpm/run-npm.svg)](https://www.npmjs.com/package/@pnpm/run-npm) - -## Installation - -```sh -pnpm add @pnpm/run-npm -``` - -## License - -MIT diff --git a/exec/run-npm/package.json b/exec/run-npm/package.json deleted file mode 100644 index 74a7544986..0000000000 --- a/exec/run-npm/package.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "@pnpm/exec.run-npm", - "version": "1000.0.0", - "description": "Runs the npm CLI", - "keywords": [ - "pnpm", - "pnpm11" - ], - "license": "MIT", - "funding": "https://opencollective.com/pnpm", - "repository": "https://github.com/pnpm/pnpm/tree/main/exec/run-npm", - "homepage": "https://github.com/pnpm/pnpm/tree/main/exec/run-npm#readme", - "bugs": { - "url": "https://github.com/pnpm/pnpm/issues" - }, - "type": "module", - "main": "lib/index.js", - "types": "lib/index.d.ts", - "exports": { - ".": "./lib/index.js" - }, - "files": [ - "lib", - "!*.map" - ], - "scripts": { - "lint": "eslint \"src/**/*.ts\"", - "test": "pn compile", - "prepublishOnly": "pn compile", - "compile": "tsgo --build && pn lint --fix" - }, - "dependencies": { - "cross-spawn": "catalog:", - "path-name": "catalog:" - }, - "devDependencies": { - "@pnpm/exec.run-npm": "workspace:*", - "@types/cross-spawn": "catalog:" - }, - "engines": { - "node": ">=22.13" - }, - "jest": { - "preset": "@pnpm/jest-config" - } -} diff --git a/exec/run-npm/src/index.ts b/exec/run-npm/src/index.ts deleted file mode 100644 index 5f0cd127fd..0000000000 --- a/exec/run-npm/src/index.ts +++ /dev/null @@ -1,81 +0,0 @@ -import type childProcess from 'node:child_process' -import path from 'node:path' - -import spawn from 'cross-spawn' -import PATH from 'path-name' - -export type NPMLocation = 'global' | 'user' | 'project' - -export interface RunNPMOptions { - cwd?: string - env?: Record - location?: NPMLocation - userConfigPath?: string -} - -export function runNpm (npmPath: string | undefined, args: string[], options?: RunNPMOptions): childProcess.SpawnSyncReturns { - const npm = npmPath ?? 'npm' - return runScriptSync(npm, args, { - cwd: options?.cwd ?? process.cwd(), - stdio: 'inherit', - userAgent: undefined, - env: { ...options?.env, COREPACK_ENABLE_STRICT: '0' }, - location: options?.location, - userConfigPath: options?.userConfigPath, - }) -} - -export function runScriptSync ( - command: string, - args: string[], - opts: { - cwd: string - location?: NPMLocation - stdio: childProcess.StdioOptions - userAgent?: string - userConfigPath?: string - env: Record - } -): childProcess.SpawnSyncReturns { - const env = { - ...createEnv(opts), - ...opts.env, - } - const result = spawn.sync(command, args, { - ...opts, - env, - }) - if (result.error) throw result.error - return result -} - -function createEnv ( - opts: { - cwd: string - location?: NPMLocation - userAgent?: string - userConfigPath?: string - } -): NodeJS.ProcessEnv { - const env = { ...process.env } - - env[PATH] = [ - path.join(opts.cwd, 'node_modules', '.bin'), - path.dirname(process.execPath), - process.env[PATH], - ].join(path.delimiter) - - if (opts.userAgent) { - env.npm_config_user_agent = opts.userAgent - } - - if (opts.location) { - env.npm_config_location = opts.location - } - - if (opts.userConfigPath) { - env.npm_config_userconfig = opts.userConfigPath - } - - return env -} diff --git a/exec/run-npm/tsconfig.json b/exec/run-npm/tsconfig.json deleted file mode 100644 index c6f0399f60..0000000000 --- a/exec/run-npm/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "@pnpm/tsconfig", - "compilerOptions": { - "outDir": "lib", - "rootDir": "src" - }, - "include": [ - "src/**/*.ts", - "../../__typings__/**/*.d.ts" - ], - "references": [] -} diff --git a/exec/run-npm/tsconfig.lint.json b/exec/run-npm/tsconfig.lint.json deleted file mode 100644 index 1bbe711971..0000000000 --- a/exec/run-npm/tsconfig.lint.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": [ - "src/**/*.ts", - "test/**/*.ts", - "../../__typings__/**/*.d.ts" - ] -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4989b732e7..c9e0e0e39d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2275,9 +2275,6 @@ importers: '@pnpm/error': specifier: workspace:* version: link:../../core/error - '@pnpm/exec.run-npm': - specifier: workspace:* - version: link:../../exec/run-npm '@pnpm/object.key-sorting': specifier: workspace:* version: link:../../object/key-sorting @@ -4128,22 +4125,6 @@ importers: specifier: 'catalog:' version: 7.0.1 - exec/run-npm: - dependencies: - cross-spawn: - specifier: 'catalog:' - version: 7.0.6 - path-name: - specifier: 'catalog:' - version: 1.0.0 - devDependencies: - '@pnpm/exec.run-npm': - specifier: workspace:* - version: 'link:' - '@types/cross-spawn': - specifier: 'catalog:' - version: 6.0.6 - fetching/binary-fetcher: dependencies: '@pnpm/error': @@ -7231,9 +7212,6 @@ importers: '@pnpm/exec.commands': specifier: workspace:* version: link:../exec/commands - '@pnpm/exec.run-npm': - specifier: workspace:* - version: link:../exec/run-npm '@pnpm/hooks.pnpmfile': specifier: workspace:* version: link:../hooks/pnpmfile diff --git a/pnpm/package.json b/pnpm/package.json index f85d3afd17..e3ce26147b 100644 --- a/pnpm/package.json +++ b/pnpm/package.json @@ -102,7 +102,6 @@ "@pnpm/engine.runtime.commands": "workspace:*", "@pnpm/error": "workspace:*", "@pnpm/exec.commands": "workspace:*", - "@pnpm/exec.run-npm": "workspace:*", "@pnpm/hooks.pnpmfile": "workspace:*", "@pnpm/installing.client": "workspace:*", "@pnpm/installing.commands": "workspace:*", diff --git a/pnpm/tsconfig.json b/pnpm/tsconfig.json index c5599b3209..528a2526ca 100644 --- a/pnpm/tsconfig.json +++ b/pnpm/tsconfig.json @@ -98,9 +98,6 @@ { "path": "../exec/commands" }, - { - "path": "../exec/run-npm" - }, { "path": "../hooks/pnpmfile" },