mirror of
https://github.com/pnpm/pnpm.git
synced 2025-12-24 07:38:12 -05:00
fix: link local bins created by postinstall scripts (#6728)
close #1801
This commit is contained in:
9
.changeset/plenty-bags-fix.md
Normal file
9
.changeset/plenty-bags-fix.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
"@pnpm/plugin-commands-rebuild": patch
|
||||
"@pnpm/headless": patch
|
||||
"@pnpm/core": patch
|
||||
"@pnpm/lifecycle": patch
|
||||
"pnpm": patch
|
||||
---
|
||||
|
||||
Local workspace bin files that should be compiled first are linked to dependent projects after compilation [#1801](https://github.com/pnpm/pnpm/issues/1801).
|
||||
@@ -38,6 +38,7 @@
|
||||
"@pnpm/core-loggers": "workspace:*",
|
||||
"@pnpm/directory-fetcher": "workspace:*",
|
||||
"@pnpm/error": "workspace:*",
|
||||
"@pnpm/link-bins": "workspace:*",
|
||||
"@pnpm/npm-lifecycle": "^2.0.1",
|
||||
"@pnpm/read-package-json": "workspace:*",
|
||||
"@pnpm/store-controller-types": "workspace:*",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import fs from 'fs'
|
||||
import { linkBins } from '@pnpm/link-bins'
|
||||
import { logger } from '@pnpm/logger'
|
||||
import path from 'path'
|
||||
import { fetchFromDir } from '@pnpm/directory-fetcher'
|
||||
import { type StoreController } from '@pnpm/store-controller-types'
|
||||
@@ -13,6 +15,8 @@ export type RunLifecycleHooksConcurrentlyOptions = Omit<RunLifecycleHookOptions,
|
||||
> & {
|
||||
resolveSymlinksInInjectedDirs?: boolean
|
||||
storeController: StoreController
|
||||
extraNodePaths?: string[]
|
||||
preferSymlinkedExecutables?: boolean
|
||||
}
|
||||
|
||||
export interface Importer {
|
||||
@@ -43,6 +47,16 @@ export async function runLifecycleHooksConcurrently (
|
||||
const importers = importersByBuildIndex.get(buildIndex)!
|
||||
return importers.map(({ manifest, modulesDir, rootDir, stages: importerStages, targetDirs }) =>
|
||||
async () => {
|
||||
// We are linking the bin files, in case they were created by lifecycle scripts of other workspace packages.
|
||||
await linkBins(modulesDir, path.join(modulesDir, '.bin'), {
|
||||
extraNodePaths: opts.extraNodePaths,
|
||||
allowExoticManifests: true,
|
||||
preferSymlinkedExecutables: opts.preferSymlinkedExecutables,
|
||||
projectManifest: manifest,
|
||||
warn: (message: string) => {
|
||||
logger.warn({ message, prefix: rootDir })
|
||||
},
|
||||
})
|
||||
const runLifecycleHookOpts = {
|
||||
...opts,
|
||||
depPath: rootDir,
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
{
|
||||
"path": "../../packages/types"
|
||||
},
|
||||
{
|
||||
"path": "../../pkg-manager/link-bins"
|
||||
},
|
||||
{
|
||||
"path": "../../pkg-manifest/read-package-json"
|
||||
},
|
||||
|
||||
@@ -14,6 +14,7 @@ export interface StrictRebuildOptions {
|
||||
extraEnv: Record<string, string>
|
||||
lockfileDir: string
|
||||
nodeLinker: 'isolated' | 'hoisted' | 'pnp'
|
||||
preferSymlinkedExecutables?: boolean
|
||||
scriptShell?: string
|
||||
sideEffectsCacheRead: boolean
|
||||
scriptsPrependNodePath: boolean | 'warn-only'
|
||||
|
||||
@@ -157,7 +157,9 @@ export async function rebuildProjects (
|
||||
const store = await createOrConnectStoreController(opts)
|
||||
const scriptsOpts = {
|
||||
extraBinPaths: ctx.extraBinPaths,
|
||||
extraNodePaths: ctx.extraNodePaths,
|
||||
extraEnv: opts.extraEnv,
|
||||
preferSymlinkedExecutables: opts.preferSymlinkedExecutables,
|
||||
rawConfig: opts.rawConfig,
|
||||
scriptsPrependNodePath: opts.scriptsPrependNodePath,
|
||||
scriptShell: opts.scriptShell,
|
||||
|
||||
@@ -293,7 +293,9 @@ export async function mutateModules (
|
||||
async function _install (): Promise<{ updatedProjects: UpdatedProject[], stats?: InstallationResultStats }> {
|
||||
const scriptsOpts: RunLifecycleHooksConcurrentlyOptions = {
|
||||
extraBinPaths: opts.extraBinPaths,
|
||||
extraNodePaths: ctx.extraNodePaths,
|
||||
extraEnv: opts.extraEnv,
|
||||
preferSymlinkedExecutables: opts.preferSymlinkedExecutables,
|
||||
rawConfig: opts.rawConfig,
|
||||
resolveSymlinksInInjectedDirs: opts.resolveSymlinksInInjectedDirs,
|
||||
scriptsPrependNodePath: opts.scriptsPrependNodePath,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { assertProject } from '@pnpm/assert-project'
|
||||
import { LOCKFILE_VERSION_V6 as LOCKFILE_VERSION } from '@pnpm/constants'
|
||||
@@ -1894,3 +1895,67 @@ test('do not symlink local package from the location described in its publishCon
|
||||
const linkedManifest = await loadJsonFile<{ name: string }>('project-2/node_modules/project-1/package.json')
|
||||
expect(linkedManifest.name).toBe('project-1')
|
||||
})
|
||||
|
||||
test('link the bin file of a workspace project that is created by a lifecycle script', async () => {
|
||||
const pkg1 = {
|
||||
name: 'project-1',
|
||||
version: '1.0.0',
|
||||
scripts: {
|
||||
prepare: 'bin',
|
||||
},
|
||||
|
||||
dependencies: {
|
||||
'project-2': 'link:../project-2',
|
||||
},
|
||||
}
|
||||
const pkg2 = {
|
||||
name: 'project-2',
|
||||
version: '1.0.0',
|
||||
bin: {
|
||||
bin: 'bin.js',
|
||||
},
|
||||
scripts: {
|
||||
prepare: 'node -e "require(\'fs\').renameSync(\'__bin.js\', \'bin.js\')"',
|
||||
},
|
||||
|
||||
dependencies: {},
|
||||
}
|
||||
preparePackages([pkg1, pkg2])
|
||||
fs.writeFileSync('project-2/__bin.js', `#!/usr/bin/env node
|
||||
require("fs").writeFileSync("created-by-prepare", "", "utf8")`)
|
||||
|
||||
const importers: MutatedProject[] = [
|
||||
{
|
||||
mutation: 'install',
|
||||
rootDir: path.resolve('project-1'),
|
||||
},
|
||||
{
|
||||
mutation: 'install',
|
||||
rootDir: path.resolve('project-2'),
|
||||
},
|
||||
]
|
||||
const allProjects = [
|
||||
{
|
||||
buildIndex: 1,
|
||||
manifest: pkg1,
|
||||
rootDir: path.resolve('project-1'),
|
||||
},
|
||||
{
|
||||
buildIndex: 0,
|
||||
manifest: pkg2,
|
||||
rootDir: path.resolve('project-2'),
|
||||
},
|
||||
]
|
||||
await mutateModules(importers, await testDefaults({ allProjects }))
|
||||
|
||||
expect(fs.existsSync('project-1/created-by-prepare')).toBeTruthy()
|
||||
|
||||
await rimraf('node_modules')
|
||||
await rimraf('project-1/node_modules')
|
||||
await rimraf('project-2/node_modules')
|
||||
fs.renameSync('project-2/bin.js', 'project-2/__bin.js')
|
||||
|
||||
await mutateModules(importers, await testDefaults({ allProjects, frozenLockfile: true }))
|
||||
|
||||
expect(fs.existsSync('project-1/created-by-prepare')).toBeTruthy()
|
||||
})
|
||||
|
||||
@@ -195,6 +195,8 @@ export async function headlessInstall (opts: HeadlessOptions): Promise<Installat
|
||||
const scriptsOpts = {
|
||||
optional: false,
|
||||
extraBinPaths: opts.extraBinPaths,
|
||||
extraNodePaths: opts.extraNodePaths,
|
||||
preferSymlinkedExecutables: opts.preferSymlinkedExecutables,
|
||||
extraEnv: opts.extraEnv,
|
||||
rawConfig: opts.rawConfig,
|
||||
resolveSymlinksInInjectedDirs: opts.resolveSymlinksInInjectedDirs,
|
||||
|
||||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@@ -1020,6 +1020,9 @@ importers:
|
||||
'@pnpm/error':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/error
|
||||
'@pnpm/link-bins':
|
||||
specifier: workspace:*
|
||||
version: link:../../pkg-manager/link-bins
|
||||
'@pnpm/logger':
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.0
|
||||
|
||||
Reference in New Issue
Block a user