fix(headless): postinstall script should run after root deps are linked (#4026)

close #4018
This commit is contained in:
Zoltan Kochan
2021-11-22 02:46:54 +02:00
committed by GitHub
parent 5a11c8bac4
commit b7fbd8c330
5 changed files with 142 additions and 92 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/headless": patch
"pnpm": patch
---
The postinstall scripts of dependencies should be executed after the root dependencies of the project are symlinked [#4018](https://github.com/pnpm/pnpm/issues/4018).

View File

@@ -35,7 +35,7 @@
"@commitlint/prompt-cli": "^15.0.0",
"@pnpm/eslint-config": "workspace:*",
"@pnpm/meta-updater": "0.0.6",
"@pnpm/registry-mock": "^2.9.0",
"@pnpm/registry-mock": "^2.10.0",
"@pnpm/tsconfig": "workspace:*",
"@types/jest": "^26.0.24",
"@types/node": "^14.17.32",

View File

@@ -511,3 +511,42 @@ test('lifecycle scripts have access to package\'s own binary by binary name', as
await project.isExecutable('.pnpm/runs-own-bin@1.0.0/node_modules/runs-own-bin/node_modules/.bin/runs-own-bin')
})
test('lifecycle scripts run after linking root dependencies', async () => {
prepareEmpty()
const manifest = {
dependencies: {
'is-positive': '1.0.0',
'postinstall-requires-is-positive': '1.0.0',
},
}
await mutateModules(
[
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
],
await testDefaults({ fastUnpack: false })
)
await rimraf('node_modules')
await mutateModules(
[
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
],
await testDefaults({ fastUnpack: false, frozenLockfile: true })
)
// if there was no exception, the test passed
})

View File

@@ -259,6 +259,7 @@ export default async (opts: HeadlessOptions) => {
})
}
let newHoistedDependencies!: HoistedDependencies
if (opts.enableModulesDir !== false) {
await Promise.all(depNodes.map(async (depNode) => fs.mkdir(depNode.modules, { recursive: true })))
await Promise.all([
@@ -280,7 +281,6 @@ export default async (opts: HeadlessOptions) => {
stage: 'importing_done',
})
let newHoistedDependencies!: HoistedDependencies
if (opts.ignorePackageManifest !== true && (opts.hoistPattern != null || opts.publicHoistPattern != null)) {
// It is important to keep the skipped packages in the lockfile which will be saved as the "current lockfile".
// pnpm is comparing the current lockfile to the wanted one and they should much.
@@ -304,84 +304,11 @@ export default async (opts: HeadlessOptions) => {
newHoistedDependencies = {}
}
if (opts.ignoreScripts) {
for (const { id, manifest } of opts.projects) {
if (opts.ignoreScripts && ((manifest?.scripts) != null) &&
(manifest.scripts.preinstall ?? manifest.scripts.prepublish ??
manifest.scripts.install ??
manifest.scripts.postinstall ??
manifest.scripts.prepare)
) {
opts.pendingBuilds.push(id)
}
}
// we can use concat here because we always only append new packages, which are guaranteed to not be there by definition
opts.pendingBuilds = opts.pendingBuilds
.concat(
depNodes
.filter(({ requiresBuild }) => requiresBuild)
.map(({ depPath }) => depPath)
)
} else {
const directNodes = new Set<string>()
for (const id of importerIds) {
Object
.values(directDependenciesByImporterId[id])
.filter((loc) => graph[loc])
.forEach((loc) => {
directNodes.add(loc)
})
}
const extraBinPaths = [...opts.extraBinPaths ?? []]
if (opts.hoistPattern != null) {
extraBinPaths.unshift(path.join(virtualStoreDir, 'node_modules/.bin'))
}
let extraEnv: Record<string, string> | undefined
if (opts.enablePnp) {
extraEnv = makeNodeRequireOption(path.join(opts.lockfileDir, '.pnp.cjs'))
}
await buildModules(graph, Array.from(directNodes), {
childConcurrency: opts.childConcurrency,
extraBinPaths,
extendNodePath: opts.extendNodePath,
extraEnv,
lockfileDir,
optional: opts.include.optionalDependencies,
rawConfig: opts.rawConfig,
rootModulesDir: virtualStoreDir,
scriptsPrependNodePath: opts.scriptsPrependNodePath,
scriptShell: opts.scriptShell,
shellEmulator: opts.shellEmulator,
sideEffectsCacheWrite: opts.sideEffectsCacheWrite,
storeController: opts.storeController,
unsafePerm: opts.unsafePerm,
userAgent: opts.userAgent,
})
}
await writeModulesYaml(rootModulesDir, {
hoistedDependencies: newHoistedDependencies,
hoistPattern: opts.hoistPattern,
included: opts.include,
layoutVersion: LAYOUT_VERSION,
packageManager: `${opts.packageManager.name}@${opts.packageManager.version}`,
pendingBuilds: opts.pendingBuilds,
publicHoistPattern: opts.publicHoistPattern,
prunedAt: opts.pruneVirtualStore === true || opts.prunedAt == null
? new Date().toUTCString()
: opts.prunedAt,
registries: opts.registries,
skipped: Array.from(skipped),
storeDir: opts.storeDir,
virtualStoreDir,
})
await linkAllBins(graph, { extendNodePath: opts.extendNodePath, optional: opts.include.optionalDependencies, warn })
if ((currentLockfile != null) && !equals(importerIds.sort(), Object.keys(filteredLockfile.importers).sort())) {
Object.assign(filteredLockfile.packages, currentLockfile.packages)
}
await writeCurrentLockfile(virtualStoreDir, filteredLockfile)
/** Skip linking and due to no project manifest */
if (!opts.ignorePackageManifest) {
@@ -405,7 +332,67 @@ export default async (opts: HeadlessOptions) => {
updated: manifest,
})
}))
}
}
if (opts.ignoreScripts) {
for (const { id, manifest } of opts.projects) {
if (opts.ignoreScripts && ((manifest?.scripts) != null) &&
(manifest.scripts.preinstall ?? manifest.scripts.prepublish ??
manifest.scripts.install ??
manifest.scripts.postinstall ??
manifest.scripts.prepare)
) {
opts.pendingBuilds.push(id)
}
}
// we can use concat here because we always only append new packages, which are guaranteed to not be there by definition
opts.pendingBuilds = opts.pendingBuilds
.concat(
depNodes
.filter(({ requiresBuild }) => requiresBuild)
.map(({ depPath }) => depPath)
)
} else {
const directNodes = new Set<string>()
for (const id of importerIds) {
Object
.values(directDependenciesByImporterId[id])
.filter((loc) => graph[loc])
.forEach((loc) => {
directNodes.add(loc)
})
}
const extraBinPaths = [...opts.extraBinPaths ?? []]
if (opts.hoistPattern != null) {
extraBinPaths.unshift(path.join(virtualStoreDir, 'node_modules/.bin'))
}
let extraEnv: Record<string, string> | undefined
if (opts.enablePnp) {
extraEnv = makeNodeRequireOption(path.join(opts.lockfileDir, '.pnp.cjs'))
}
await buildModules(graph, Array.from(directNodes), {
childConcurrency: opts.childConcurrency,
extraBinPaths,
extendNodePath: opts.extendNodePath,
extraEnv,
lockfileDir,
optional: opts.include.optionalDependencies,
rawConfig: opts.rawConfig,
rootModulesDir: virtualStoreDir,
scriptsPrependNodePath: opts.scriptsPrependNodePath,
scriptShell: opts.scriptShell,
shellEmulator: opts.shellEmulator,
sideEffectsCacheWrite: opts.sideEffectsCacheWrite,
storeController: opts.storeController,
unsafePerm: opts.unsafePerm,
userAgent: opts.userAgent,
})
}
if (opts.enableModulesDir !== false) {
/** Skip linking and due to no project manifest */
if (!opts.ignorePackageManifest) {
await Promise.all(opts.projects.map(async (project) => {
if (opts.publicHoistPattern?.length && path.relative(opts.lockfileDir, project.rootDir) === '') {
await linkBinsOfImporter(project, { extendNodePath: opts.extendNodePath })
@@ -427,7 +414,25 @@ export default async (opts: HeadlessOptions) => {
}
}))
}
await writeModulesYaml(rootModulesDir, {
hoistedDependencies: newHoistedDependencies,
hoistPattern: opts.hoistPattern,
included: opts.include,
layoutVersion: LAYOUT_VERSION,
packageManager: `${opts.packageManager.name}@${opts.packageManager.version}`,
pendingBuilds: opts.pendingBuilds,
publicHoistPattern: opts.publicHoistPattern,
prunedAt: opts.pruneVirtualStore === true || opts.prunedAt == null
? new Date().toUTCString()
: opts.prunedAt,
registries: opts.registries,
skipped: Array.from(skipped),
storeDir: opts.storeDir,
virtualStoreDir,
})
await writeCurrentLockfile(virtualStoreDir, filteredLockfile)
}
// waiting till package requests are finished
await Promise.all(depNodes.map(({ finishing }) => finishing))

34
pnpm-lock.yaml generated
View File

@@ -40,7 +40,7 @@ importers:
'@commitlint/prompt-cli': ^15.0.0
'@pnpm/eslint-config': workspace:*
'@pnpm/meta-updater': 0.0.6
'@pnpm/registry-mock': ^2.9.0
'@pnpm/registry-mock': ^2.10.0
'@pnpm/tsconfig': workspace:*
'@types/jest': ^26.0.24
'@types/node': ^14.17.32
@@ -71,7 +71,7 @@ importers:
'@commitlint/prompt-cli': 15.0.0
'@pnpm/eslint-config': link:utils/eslint-config
'@pnpm/meta-updater': 0.0.6
'@pnpm/registry-mock': 2.9.0
'@pnpm/registry-mock': 2.10.0
'@pnpm/tsconfig': link:utils/tsconfig
'@types/jest': 26.0.24
'@types/node': 14.17.34
@@ -4645,15 +4645,15 @@ packages:
load-json-file: 6.2.0
dev: true
/@pnpm/cli-utils/0.6.31_@pnpm+logger@4.0.0:
resolution: {integrity: sha512-SrCdFnD5dbyyAzqm5hnOY2S/6/+zxs7nkuVQWYdhNWiAmwxlVy5ip9ZYlwMzhUHVnlTXtOwelrJm7GlaVFu84g==}
/@pnpm/cli-utils/0.6.32_@pnpm+logger@4.0.0:
resolution: {integrity: sha512-zHX4ozJeuEWWH/i9/nekXxpKduXJ9buRNEppR9X3A4Ir/a4je4Feyjf9gPXwa/ZB5dbOerweQYlK0SB5nQuUKg==}
engines: {node: '>=12.17'}
peerDependencies:
'@pnpm/logger': ^4.0.0
dependencies:
'@pnpm/cli-meta': 2.0.0
'@pnpm/config': 13.4.2_@pnpm+logger@4.0.0
'@pnpm/default-reporter': 8.3.6_@pnpm+logger@4.0.0
'@pnpm/config': 13.5.0_@pnpm+logger@4.0.0
'@pnpm/default-reporter': 8.3.7_@pnpm+logger@4.0.0
'@pnpm/error': 2.0.0
'@pnpm/logger': 4.0.0
'@pnpm/manifest-utils': 2.1.2_@pnpm+logger@4.0.0
@@ -4670,8 +4670,8 @@ packages:
chalk: 4.1.2
dev: false
/@pnpm/config/13.4.2_@pnpm+logger@4.0.0:
resolution: {integrity: sha512-SO1EwqIJFE3DdeihtNAPlqodxR0hRZ/XY++qc8wJKdnbHlX4nuLT7eaQ2t60l66EVSA05yaYzMjUwahPdYFVUA==}
/@pnpm/config/13.5.0_@pnpm+logger@4.0.0:
resolution: {integrity: sha512-CGcTb8hQtAAhCQ22MFiJ5lx/B67ZYnE9Xo+n1jDCJBy2z9YRqzXv7YpKd8ez2HsHkqEB1mehheD1pN5vxuaPJg==}
engines: {node: '>=12.17'}
dependencies:
'@pnpm/constants': 5.0.0
@@ -4705,11 +4705,11 @@ packages:
'@pnpm/types': 7.6.0
dev: true
/@pnpm/default-reporter/8.3.6_@pnpm+logger@4.0.0:
resolution: {integrity: sha512-dq0vb3Brv+MxvcH8TSBLVaasgS3XvJPL+ORD/nPvinDnmiSiT+qGn/OIwO0Ib6lpIzvWXv93+OPPuQ9Jnpb/vA==}
/@pnpm/default-reporter/8.3.7_@pnpm+logger@4.0.0:
resolution: {integrity: sha512-J1B+Q77AvAW81e3x6nNBZEbVudeEbaUrILtf9UJQLVJy+wU2nUHaF0uWVK++B/0utx262DRt0DieDeQMJaZ60Q==}
engines: {node: '>=12.17'}
dependencies:
'@pnpm/config': 13.4.2_@pnpm+logger@4.0.0
'@pnpm/config': 13.5.0_@pnpm+logger@4.0.0
'@pnpm/core-loggers': 6.0.6_@pnpm+logger@4.0.0
'@pnpm/error': 2.0.0
'@pnpm/types': 7.6.0
@@ -4752,11 +4752,11 @@ packages:
find-up: 5.0.0
dev: true
/@pnpm/find-workspace-packages/3.1.23_@pnpm+logger@4.0.0:
resolution: {integrity: sha512-22z6tgybQ9I/UA9FNPJ1S3wtYa36RxF2xpjfqZNv3l7Q9P651duhevP7uUWyUYeQ0xT8259D+PpdOTHQhSpcpQ==}
/@pnpm/find-workspace-packages/3.1.24_@pnpm+logger@4.0.0:
resolution: {integrity: sha512-Ik8FsWp3sXGQHGQPGqM9MAr3WEoaIZXIec/k58/F9RHqmaTfKmP60B444grpS8xGVeNXyYkKqKCvsf7Wr/tHag==}
engines: {node: '>=12.17'}
dependencies:
'@pnpm/cli-utils': 0.6.31_@pnpm+logger@4.0.0
'@pnpm/cli-utils': 0.6.32_@pnpm+logger@4.0.0
'@pnpm/constants': 5.0.0
'@pnpm/types': 7.6.0
find-packages: 8.0.7
@@ -4812,7 +4812,7 @@ packages:
hasBin: true
dependencies:
'@pnpm/find-workspace-dir': 3.0.1
'@pnpm/find-workspace-packages': 3.1.23_@pnpm+logger@4.0.0
'@pnpm/find-workspace-packages': 3.1.24_@pnpm+logger@4.0.0
'@pnpm/logger': 4.0.0
'@pnpm/types': 7.6.0
load-json-file: 6.2.0
@@ -4893,8 +4893,8 @@ packages:
strip-bom: 4.0.0
dev: true
/@pnpm/registry-mock/2.9.0:
resolution: {integrity: sha512-o3jkMVvyZ/kGZXVQ4f+8ENH2p2A/AIH4kAz0d4ULxtCeXS5ujRIqCfwaBYIU2NfJrZP8lSMYsLBat1egXJV+GA==}
/@pnpm/registry-mock/2.10.0:
resolution: {integrity: sha512-96frA3W36rjd6BRIjKmkXKTLIlpZFogvkbKnYp68+K2RWBoqDQd789ZZbJvz6Tz3fNWYcKpz6lUI4Bm3S+jtwA==}
engines: {node: '>=10.13'}
hasBin: true
dependencies: