fix: set NODE_PATH when is turned on (#5251)

Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
Dominic Elm
2022-08-24 00:44:57 +02:00
committed by GitHub
parent a12d1a0113
commit 2aa22e4b17
29 changed files with 143 additions and 13 deletions

View File

@@ -0,0 +1,15 @@
---
"@pnpm/config": minor
"@pnpm/core": minor
"@pnpm/headless": minor
"@pnpm/plugin-commands-deploy": minor
"@pnpm/plugin-commands-installation": minor
"@pnpm/plugin-commands-listing": minor
"@pnpm/plugin-commands-outdated": minor
"@pnpm/plugin-commands-patching": minor
"@pnpm/plugin-commands-publishing": minor
"@pnpm/plugin-commands-rebuild": minor
"@pnpm/plugin-commands-script-runners": minor
---
Set `NODE_PATH` when `preferSymlinkedExecutables` is enabled.

View File

@@ -44,6 +44,7 @@
"can-write-to-dir": "^1.1.1",
"is-subdir": "^1.2.0",
"normalize-registry-url": "2.0.0",
"path-absolute": "^1.0.1",
"path-name": "^1.0.0",
"ramda": "npm:@pnpm/ramda@0.28.1",
"realpath-missing": "^1.1.0",

View File

@@ -19,6 +19,7 @@ export interface Config {
cliOptions: Record<string, any>, // eslint-disable-line
useBetaCli: boolean
extraBinPaths: string[]
extraEnv: Record<string, string>
filter: string[]
filterProd: string[]
rawLocalConfig: Record<string, any>, // eslint-disable-line

View File

@@ -12,6 +12,7 @@ import camelcase from 'camelcase'
import normalizeRegistryUrl from 'normalize-registry-url'
import fromPairs from 'ramda/src/fromPairs'
import realpathMissing from 'realpath-missing'
import pathAbsolute from 'path-absolute'
import whichcb from 'which'
import { checkGlobalBinDir } from './checkGlobalBinDir'
import getScopeRegistries from './getScopeRegistries'
@@ -402,6 +403,21 @@ export default async (
} else {
pnpmConfig.extraBinPaths = []
}
if (pnpmConfig.preferSymlinkedExecutables) {
const cwd = pnpmConfig.lockfileDir ?? pnpmConfig.dir
const virtualStoreDir = pnpmConfig.virtualStoreDir
? pnpmConfig.virtualStoreDir
: pnpmConfig.modulesDir
? path.join(pnpmConfig.modulesDir, '.pnpm')
: 'node_modules/.pnpm'
pnpmConfig.extraEnv = {
NODE_PATH: pathAbsolute(path.join(virtualStoreDir, 'node_modules'), cwd),
}
}
if (pnpmConfig['shamefullyFlatten']) {
warnings.push('The "shamefully-flatten" setting has been renamed to "shamefully-hoist". Also, in most cases you won\'t need "shamefully-hoist". Since v4, a semistrict node_modules structure is on by default (via hoist-pattern=[*]).')
pnpmConfig.shamefullyHoist = true

View File

@@ -24,6 +24,7 @@ export interface StrictInstallOptions {
frozenLockfileIfExists: boolean
enablePnp: boolean
extraBinPaths: string[]
extraEnv: Record<string, string>
hoistingLimits?: HoistingLimits
useLockfile: boolean
saveLockfile: boolean

View File

@@ -228,6 +228,7 @@ export async function mutateModules (
async function _install (): Promise<UpdatedProject[]> {
const scriptsOpts: RunLifecycleHooksConcurrentlyOptions = {
extraBinPaths: opts.extraBinPaths,
extraEnv: opts.extraEnv,
rawConfig: opts.rawConfig,
scriptsPrependNodePath: opts.scriptsPrependNodePath,
scriptShell: opts.scriptShell,
@@ -961,9 +962,12 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
const depPaths = Object.keys(dependenciesGraph)
const rootNodes = depPaths.filter((depPath) => dependenciesGraph[depPath].depth === 0)
let extraEnv: Record<string, string> | undefined
let extraEnv: Record<string, string> | undefined = opts.scriptsOpts.extraEnv
if (opts.enablePnp) {
extraEnv = makeNodeRequireOption(path.join(opts.lockfileDir, '.pnp.cjs'))
extraEnv = {
...extraEnv,
...makeNodeRequireOption(path.join(opts.lockfileDir, '.pnp.cjs')),
}
}
await buildModules(dependenciesGraph, rootNodes, {
childConcurrency: opts.childConcurrency,
@@ -1107,7 +1111,10 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
])
if (!opts.ignoreScripts) {
if (opts.enablePnp) {
opts.scriptsOpts.extraEnv = makeNodeRequireOption(path.join(opts.lockfileDir, '.pnp.cjs'))
opts.scriptsOpts.extraEnv = {
...opts.scriptsOpts.extraEnv,
...makeNodeRequireOption(path.join(opts.lockfileDir, '.pnp.cjs')),
}
}
const projectsToBeBuilt = projectsWithTargetDirs.filter(({ mutation }) => mutation === 'install') as ProjectToBeInstalled[]
await runLifecycleHooksConcurrently(['preinstall', 'install', 'postinstall', 'prepare'],

View File

@@ -100,6 +100,7 @@ export interface HeadlessOptions {
enablePnp?: boolean
engineStrict: boolean
extraBinPaths?: string[]
extraEnv?: Record<string, string>
extraNodePaths?: string[]
preferSymlinkedExecutables?: boolean
hoistingLimits?: HoistingLimits
@@ -186,6 +187,7 @@ export default async (opts: HeadlessOptions) => {
const scriptsOpts = {
optional: false,
extraBinPaths: opts.extraBinPaths,
extraEnv: opts.extraEnv,
rawConfig: opts.rawConfig,
scriptsPrependNodePath: opts.scriptsPrependNodePath,
scriptShell: opts.scriptShell,
@@ -427,9 +429,12 @@ export default async (opts: HeadlessOptions) => {
if (opts.hoistPattern != null) {
extraBinPaths.unshift(path.join(virtualStoreDir, 'node_modules/.bin'))
}
let extraEnv: Record<string, string> | undefined
let extraEnv: Record<string, string> | undefined = opts.extraEnv
if (opts.enablePnp) {
extraEnv = makeNodeRequireOption(path.join(opts.lockfileDir, '.pnp.cjs'))
extraEnv = {
...extraEnv,
...makeNodeRequireOption(path.join(opts.lockfileDir, '.pnp.cjs')),
}
}
await buildModules(graph, Array.from(directNodes), {
childConcurrency: opts.childConcurrency,

View File

@@ -12,6 +12,7 @@ export const DEFAULT_OPTS = {
ca: undefined,
cacheDir: '../cache',
cert: undefined,
extraEnv: {},
cliOptions: {},
fetchRetries: 2,
fetchRetryFactor: 90,

View File

@@ -279,6 +279,7 @@ export type InstallCommandOptions = Pick<Config,
| 'virtualStoreDir'
| 'workspaceConcurrency'
| 'workspaceDir'
| 'extraEnv'
> & CreateStoreControllerOptions & {
argv: {
original: string[]

View File

@@ -75,6 +75,7 @@ export type InstallDepsOptions = Pick<Config,
| 'optional'
| 'workspaceConcurrency'
| 'workspaceDir'
| 'extraEnv'
> & CreateStoreControllerOptions & {
argv: {
original: string[]

View File

@@ -16,6 +16,7 @@ const DEFAULT_OPTIONS = {
bail: false,
bin: 'node_modules/.bin',
cacheDir: path.join(tmp, 'cache'),
extraEnv: {},
cliOptions: {},
include: {
dependencies: true,

View File

@@ -16,6 +16,7 @@ const DEFAULT_OPTIONS = {
bail: false,
bin: 'node_modules/.bin',
cacheDir: path.join(tmp, 'cache'),
extraEnv: {},
cliOptions: {},
include: {
dependencies: true,

View File

@@ -14,6 +14,7 @@ const DEFAULT_OPTIONS = {
bail: false,
bin: 'node_modules/.bin',
cacheDir: path.join(TMP, 'cache'),
extraEnv: {},
cliOptions: {},
include: {
dependencies: true,

View File

@@ -13,6 +13,7 @@ const DEFAULT_OPTIONS = {
},
bail: false,
bin: 'node_modules/.bin',
extraEnv: {},
cliOptions: {},
include: {
dependencies: true,

View File

@@ -25,6 +25,7 @@ const DEFAULT_OPTIONS = {
},
bail: false,
bin: 'node_modules/.bin',
extraEnv: {},
cliOptions: {},
include: {
dependencies: true,

View File

@@ -12,6 +12,7 @@ export const DEFAULT_OPTS = {
ca: undefined,
cacheDir: '../cache',
cert: undefined,
extraEnv: {},
cliOptions: {},
fetchRetries: 2,
fetchRetryFactor: 90,

View File

@@ -11,6 +11,7 @@ export const DEFAULT_OPTS = {
bin: 'node_modules/.bin',
ca: undefined,
cert: undefined,
extraEnv: {},
cliOptions: {},
fetchRetries: 2,
fetchRetryFactor: 90,

View File

@@ -12,6 +12,7 @@ export const DEFAULT_OPTS = {
ca: undefined,
cacheDir: '../cache',
cert: undefined,
extraEnv: {},
cliOptions: {},
fetchRetries: 2,
fetchRetryFactor: 90,

View File

@@ -12,6 +12,7 @@ export const DEFAULT_OPTS = {
ca: undefined,
cacheDir: '../cache',
cert: undefined,
extraEnv: {},
cliOptions: {},
fetchRetries: 2,
fetchRetryFactor: 90,

View File

@@ -171,6 +171,7 @@ Do you want to continue?`,
const _runScriptsIfPresent = runScriptsIfPresent.bind(null, {
depPath: dir,
extraBinPaths: opts.extraBinPaths,
extraEnv: opts.extraEnv,
pkgRoot: dir,
rawConfig: opts.rawConfig,
rootModulesDir: await realpathMissing(path.join(dir, 'node_modules')),

View File

@@ -27,6 +27,7 @@ Partial<Pick<Config,
| 'force'
| 'dryRun'
| 'extraBinPaths'
| 'extraEnv'
| 'fetchRetries'
| 'fetchRetryFactor'
| 'fetchRetryMaxtimeout'

View File

@@ -9,6 +9,7 @@ export interface StrictRebuildOptions {
cacheDir: string
childConcurrency: number
extraBinPaths: string[]
extraEnv: Record<string, string>
lockfileDir: string
nodeLinker: 'isolated' | 'hoisted' | 'pnp'
scriptShell?: string

View File

@@ -154,6 +154,7 @@ export async function rebuildProjects (
const store = await createOrConnectStoreController(opts)
const scriptsOpts = {
extraBinPaths: ctx.extraBinPaths,
extraEnv: opts.extraEnv,
rawConfig: opts.rawConfig,
scriptsPrependNodePath: opts.scriptsPrependNodePath,
scriptShell: opts.scriptShell,
@@ -278,6 +279,7 @@ async function _rebuild (
await runPostinstallHooks({
depPath,
extraBinPaths: ctx.extraBinPaths,
extraEnv: opts.extraEnv,
optional: pkgSnapshot.optional === true,
pkgRoot,
rawConfig: opts.rawConfig,

View File

@@ -83,7 +83,7 @@ export async function handler (
sort?: boolean
workspaceConcurrency?: number
shellMode?: boolean
} & Pick<Config, 'extraBinPaths' | 'lockfileDir' | 'dir' | 'userAgent' | 'recursive' | 'workspaceDir'>,
} & Pick<Config, 'extraBinPaths' | 'extraEnv' | 'lockfileDir' | 'dir' | 'userAgent' | 'recursive' | 'workspaceDir'>,
params: string[]
) {
// For backward compatibility
@@ -129,9 +129,10 @@ export async function handler (
limitRun(async () => {
try {
const pnpPath = workspacePnpPath ?? await existsPnp(prefix)
const extraEnv = pnpPath
? makeNodeRequireOption(pnpPath)
: {}
const extraEnv = {
...opts.extraEnv,
...(pnpPath ? makeNodeRequireOption(pnpPath) : {}),
}
const env = makeEnv({
extraEnv: {
...extraEnv,

View File

@@ -116,7 +116,7 @@ For options that may be used with `-r`, see "pnpm help recursive"',
export type RunOpts =
& Omit<RecursiveRunOpts, 'allProjects' | 'selectedProjectsGraph' | 'workspaceDir'>
& { recursive?: boolean }
& Pick<Config, 'dir' | 'engineStrict' | 'extraBinPaths' | 'reporter' | 'scriptsPrependNodePath' | 'scriptShell' | 'shellEmulator' | 'enablePrePostScripts' | 'userAgent'>
& Pick<Config, 'dir' | 'engineStrict' | 'extraBinPaths' | 'reporter' | 'scriptsPrependNodePath' | 'scriptShell' | 'shellEmulator' | 'enablePrePostScripts' | 'userAgent' | 'extraEnv'>
& (
& { recursive?: false }
& Partial<Pick<Config, 'allProjects' | 'selectedProjectsGraph' | 'workspaceDir'>>
@@ -174,6 +174,7 @@ so you may run "pnpm -w run ${scriptName}"`,
const lifecycleOpts: RunLifecycleHookOptions = {
depPath: dir,
extraBinPaths: opts.extraBinPaths,
extraEnv: opts.extraEnv,
pkgRoot: dir,
rawConfig: opts.rawConfig,
rootModulesDir: await realpathMissing(path.join(dir, 'node_modules')),
@@ -188,7 +189,10 @@ so you may run "pnpm -w run ${scriptName}"`,
const pnpPath = (opts.workspaceDir && await existsPnp(opts.workspaceDir)) ??
await existsPnp(dir)
if (pnpPath) {
lifecycleOpts.extraEnv = makeNodeRequireOption(pnpPath)
lifecycleOpts.extraEnv = {
...lifecycleOpts.extraEnv,
...makeNodeRequireOption(pnpPath),
}
}
try {
if (

View File

@@ -21,7 +21,7 @@ export type RecursiveRunOpts = Pick<Config,
| 'shellEmulator'
| 'stream'
> & Required<Pick<Config, 'allProjects' | 'selectedProjectsGraph' | 'workspaceDir'>> &
Partial<Pick<Config, 'extraBinPaths' | 'bail' | 'reverse' | 'sort' | 'workspaceConcurrency'>> &
Partial<Pick<Config, 'extraBinPaths' | 'extraEnv' | 'bail' | 'reverse' | 'sort' | 'workspaceConcurrency'>> &
{
ifPresent?: boolean
}
@@ -72,6 +72,7 @@ export default async (
const lifecycleOpts: RunLifecycleHookOptions = {
depPath: prefix,
extraBinPaths: opts.extraBinPaths,
extraEnv: opts.extraEnv,
pkgRoot: prefix,
rawConfig: opts.rawConfig,
rootModulesDir: await realpathMissing(path.join(prefix, 'node_modules')),
@@ -83,7 +84,10 @@ export default async (
}
const pnpPath = workspacePnpPath ?? await existsPnp(prefix)
if (pnpPath) {
lifecycleOpts.extraEnv = makeNodeRequireOption(pnpPath)
lifecycleOpts.extraEnv = {
...lifecycleOpts.extraEnv,
...makeNodeRequireOption(pnpPath),
}
}
if (
opts.enablePrePostScripts &&

View File

@@ -27,6 +27,7 @@ test('pnpm run: returns correct exit code', async () => {
await run.handler({
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
rawConfig: {},
}, ['exit0'])
@@ -35,6 +36,7 @@ test('pnpm run: returns correct exit code', async () => {
await run.handler({
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
rawConfig: {},
}, ['exit1'])
} catch (_err: any) { // eslint-disable-line
@@ -56,6 +58,7 @@ test('pnpm run --no-bail never fails', async () => {
bail: false,
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
rawConfig: {},
}, ['exit1'])
@@ -79,6 +82,7 @@ test('run: pass the args to the command that is specified in the build script',
await run.handler({
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
rawConfig: {},
}, ['foo', 'arg', '--flag=true', '--help', '-h'])
@@ -100,6 +104,7 @@ test('run: pass the args to the command that is specified in the build script of
await run.handler({
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
rawConfig: {},
}, ['foo', 'arg', '--flag=true', '--help', '-h'])
@@ -121,6 +126,7 @@ test('test: pass the args to the command that is specified in the build script o
await testCommand.handler({
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
rawConfig: {},
}, ['arg', '--flag=true', '--help', '-h'])
@@ -142,6 +148,7 @@ test('run start: pass the args to the command that is specified in the build scr
await run.handler({
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
rawConfig: {},
}, ['start', 'arg', '--flag=true', '--help', '-h'])
@@ -163,6 +170,7 @@ test('run stop: pass the args to the command that is specified in the build scri
await run.handler({
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
rawConfig: {},
}, ['stop', 'arg', '--flag=true', '--help', '-h'])
@@ -191,6 +199,7 @@ test('restart: run stop, restart and start', async () => {
await restart.handler({
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
rawConfig: {},
}, [])
@@ -224,6 +233,7 @@ test('restart: run stop, restart and start and all the pre/post scripts', async
dir: process.cwd(),
enablePrePostScripts: true,
extraBinPaths: [],
extraEnv: {},
rawConfig: {},
}, [])
@@ -252,6 +262,7 @@ test('"pnpm run" prints the list of available commands', async () => {
const output = await run.handler({
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
rawConfig: {},
}, [])
@@ -300,6 +311,7 @@ test('"pnpm run" prints the list of available commands, including commands of th
allProjects,
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
rawConfig: {},
selectedProjectsGraph,
workspaceDir,
@@ -326,6 +338,7 @@ Commands of the root workspace project (to run them, use "pnpm -w run"):
allProjects,
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
rawConfig: {},
selectedProjectsGraph,
workspaceDir,
@@ -348,6 +361,7 @@ test('pnpm run does not fail with --if-present even if the wanted script is not
await run.handler({
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
ifPresent: true,
rawConfig: {},
}, ['build'])
@@ -416,6 +430,7 @@ test('scripts work with PnP', async () => {
await run.handler({
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
rawConfig: {},
}, ['foo'])
@@ -443,9 +458,47 @@ test('pnpm run with custom shell', async () => {
await run.handler({
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
rawConfig: {},
scriptShell: path.resolve(`node_modules/.bin/shell-mock${isWindows() ? '.cmd' : ''}`),
}, ['build'])
expect((await import(path.resolve('shell-input.json'))).default).toStrictEqual(['-c', 'foo bar'])
})
test('pnpm run with preferSymlinkedExecutables true', async () => {
prepare({
scripts: {
build: 'node -e "console.log(process.env.NODE_PATH)"',
},
})
const npmrc = `
prefer-symlinked-executables=true=true
`
await fs.writeFile('.npmrc', npmrc, 'utf8')
const result = await execa(pnpmBin, ['run', 'build'])
expect(result.stdout).toContain(`project${path.sep}node_modules${path.sep}.pnpm${path.sep}node_modules`)
})
test('pnpm run with preferSymlinkedExecutables and custom virtualStoreDir', async () => {
prepare({
scripts: {
build: 'node -e "console.log(process.env.NODE_PATH)"',
},
})
const npmrc = `
virtual-store-dir=/foo/bar
prefer-symlinked-executables=true=true
`
await fs.writeFile('.npmrc', npmrc, 'utf8')
const result = await execa(pnpmBin, ['run', 'build'])
expect(result.stdout).toContain(`${path.sep}foo${path.sep}bar${path.sep}node_modules`)
})

View File

@@ -14,6 +14,7 @@ export const DEFAULT_OPTS = {
ca: undefined,
cacheDir: '../cache',
cert: undefined,
extraEnv: {},
cliOptions: {},
extraBinPaths: [],
fetchRetries: 2,
@@ -58,6 +59,7 @@ export const DLX_DEFAULT_OPTS = {
bail: false,
bin: 'node_modules/.bin',
cacheDir: path.join(tmp, 'cache'),
extraEnv: {},
cliOptions: {},
include: {
dependencies: true,

3
pnpm-lock.yaml generated
View File

@@ -502,6 +502,9 @@ importers:
normalize-registry-url:
specifier: 2.0.0
version: 2.0.0
path-absolute:
specifier: ^1.0.1
version: 1.0.1
path-name:
specifier: ^1.0.0
version: 1.0.0