mirror of
https://github.com/pnpm/pnpm.git
synced 2026-04-25 01:21:17 -04:00
Update all packages from zkochan/packages to their latest major versions and exclude them from minimumReleaseAge requirement. This includes updating catalog entries, adapting to breaking API changes (default exports replaced with named exports, sync functions renamed with Sync suffix), and updating type declarations.
781 lines
31 KiB
TypeScript
781 lines
31 KiB
TypeScript
import * as path from 'path'
|
|
import fs from 'fs'
|
|
import { assertProject } from '@pnpm/assert-project'
|
|
import type { LifecycleLog } from '@pnpm/core-loggers'
|
|
import { prepareEmpty, preparePackages } from '@pnpm/prepare'
|
|
import {
|
|
addDependenciesToPackage,
|
|
install,
|
|
mutateModulesInSingleProject,
|
|
type MutatedProject,
|
|
mutateModules,
|
|
} from '@pnpm/core'
|
|
import { createTestIpcServer } from '@pnpm/test-ipc-server'
|
|
import type { ProjectRootDir } from '@pnpm/types'
|
|
import { restartWorkerPool } from '@pnpm/worker'
|
|
import { jest } from '@jest/globals'
|
|
import { rimrafSync } from '@zkochan/rimraf'
|
|
import isWindows from 'is-windows'
|
|
import { loadJsonFileSync } from 'load-json-file'
|
|
import PATH from 'path-name'
|
|
import { testDefaults } from '../utils/index.js'
|
|
|
|
const testOnNonWindows = isWindows() ? test.skip : test
|
|
|
|
test('run pre/postinstall scripts', async () => {
|
|
const project = prepareEmpty()
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage({},
|
|
['@pnpm.e2e/pre-and-postinstall-scripts-example@1.0.0'],
|
|
testDefaults({ fastUnpack: false, targetDependenciesField: 'devDependencies', allowBuilds: { '@pnpm.e2e/pre-and-postinstall-scripts-example': true } })
|
|
)
|
|
|
|
{
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-prepare.js')).toBeFalsy()
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js')).toBeTruthy()
|
|
|
|
const generatedByPreinstall = project.requireModule('@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall')
|
|
expect(typeof generatedByPreinstall).toBe('function')
|
|
|
|
const generatedByPostinstall = project.requireModule('@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall')
|
|
expect(typeof generatedByPostinstall).toBe('function')
|
|
}
|
|
|
|
rimrafSync('node_modules')
|
|
|
|
// testing that the packages are not installed even though they are in lockfile
|
|
// and that their scripts are not tried to be executed
|
|
|
|
await install(manifest, testDefaults({ fastUnpack: false, production: true, allowBuilds: { '@pnpm.e2e/pre-and-postinstall-scripts-example': true } }))
|
|
|
|
{
|
|
const generatedByPreinstall = project.requireModule('@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall')
|
|
expect(typeof generatedByPreinstall).toBe('function')
|
|
|
|
const generatedByPostinstall = project.requireModule('@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall')
|
|
expect(typeof generatedByPostinstall).toBe('function')
|
|
}
|
|
})
|
|
|
|
test('return the list of packages that should be build', async () => {
|
|
prepareEmpty()
|
|
const allProjects = [
|
|
{
|
|
buildIndex: 0,
|
|
manifest: {
|
|
name: 'project',
|
|
version: '1.0.0',
|
|
|
|
dependencies: {
|
|
'@pnpm.e2e/pre-and-postinstall-scripts-example': '1.0.0',
|
|
},
|
|
},
|
|
rootDir: path.resolve('project') as ProjectRootDir,
|
|
},
|
|
]
|
|
const importers: MutatedProject[] = [
|
|
{
|
|
mutation: 'install',
|
|
rootDir: path.resolve('project') as ProjectRootDir,
|
|
},
|
|
]
|
|
const { depsRequiringBuild } = await mutateModules(importers,
|
|
testDefaults({ allProjects, enableModulesDir: false, returnListOfDepsRequiringBuild: true })
|
|
)
|
|
|
|
expect(depsRequiringBuild).toStrictEqual(['@pnpm.e2e/pre-and-postinstall-scripts-example@1.0.0'])
|
|
})
|
|
|
|
test('run pre/postinstall scripts, when PnP is used and no symlinks', async () => {
|
|
prepareEmpty()
|
|
await addDependenciesToPackage({},
|
|
['@pnpm.e2e/pre-and-postinstall-scripts-example@1.0.0'],
|
|
testDefaults({
|
|
fastUnpack: false,
|
|
enablePnp: true,
|
|
symlink: false,
|
|
targetDependenciesField: 'devDependencies',
|
|
allowBuilds: { '@pnpm.e2e/pre-and-postinstall-scripts-example': true },
|
|
})
|
|
)
|
|
|
|
const pkgDir = 'node_modules/.pnpm/@pnpm.e2e+pre-and-postinstall-scripts-example@1.0.0/node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example'
|
|
expect(fs.existsSync(path.resolve(pkgDir, 'generated-by-prepare.js'))).toBeFalsy()
|
|
expect(fs.existsSync(path.resolve(pkgDir, 'generated-by-preinstall.js'))).toBeTruthy()
|
|
expect(fs.existsSync(path.resolve(pkgDir, 'generated-by-postinstall.js'))).toBeTruthy()
|
|
})
|
|
|
|
test('testing that the bins are linked when the package with the bins was already in node_modules', async () => {
|
|
const project = prepareEmpty()
|
|
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage({}, ['@pnpm.e2e/hello-world-js-bin'], testDefaults({ fastUnpack: false }))
|
|
await addDependenciesToPackage(manifest, ['@pnpm.e2e/pre-and-postinstall-scripts-example@1.0.0'], testDefaults({ fastUnpack: false, targetDependenciesField: 'devDependencies', allowBuilds: { '@pnpm.e2e/pre-and-postinstall-scripts-example': true } }))
|
|
|
|
const generatedByPreinstall = project.requireModule('@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall')
|
|
expect(typeof generatedByPreinstall).toBe('function')
|
|
|
|
const generatedByPostinstall = project.requireModule('@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall')
|
|
expect(typeof generatedByPostinstall).toBe('function')
|
|
})
|
|
|
|
test('run install scripts', async () => {
|
|
const project = prepareEmpty()
|
|
await addDependenciesToPackage({}, ['@pnpm.e2e/install-script-example'], testDefaults({ fastUnpack: false, allowBuilds: { '@pnpm.e2e/install-script-example': true } }))
|
|
|
|
const generatedByInstall = project.requireModule('@pnpm.e2e/install-script-example/generated-by-install')
|
|
expect(typeof generatedByInstall).toBe('function')
|
|
})
|
|
|
|
test('run install scripts in the current project', async () => {
|
|
await using server = await createTestIpcServer()
|
|
await using serverForDevPreinstall = await createTestIpcServer()
|
|
prepareEmpty()
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage({
|
|
scripts: {
|
|
'pnpm:devPreinstall': `node -e "console.log('pnpm:devPreinstall-' + process.cwd())" | ${serverForDevPreinstall.generateSendStdinScript()}`,
|
|
install: `node -e "console.log('install-' + process.cwd())" | ${server.generateSendStdinScript()}`,
|
|
postinstall: `node -e "console.log('postinstall-' + process.cwd())" | ${server.generateSendStdinScript()}`,
|
|
preinstall: `node -e "console.log('preinstall-' + process.cwd())" | ${server.generateSendStdinScript()}`,
|
|
preprepare: `node -e "console.log('preprepare-' + process.cwd())" | ${server.generateSendStdinScript()}`,
|
|
postprepare: `node -e "console.log('postprepare-' + process.cwd())" | ${server.generateSendStdinScript()}`,
|
|
},
|
|
}, [], testDefaults({ fastUnpack: false }))
|
|
await install(manifest, testDefaults({ fastUnpack: false }))
|
|
|
|
expect(server.getLines()).toStrictEqual([`preinstall-${process.cwd()}`, `install-${process.cwd()}`, `postinstall-${process.cwd()}`, `preprepare-${process.cwd()}`, `postprepare-${process.cwd()}`])
|
|
expect(serverForDevPreinstall.getLines()).toStrictEqual([
|
|
// The pnpm:devPreinstall script runs twice in this test. Once for the
|
|
// initial "addDependenciesToPackage" test setup stage and again for the
|
|
// dedicated install afterwards.
|
|
`pnpm:devPreinstall-${process.cwd()}`,
|
|
`pnpm:devPreinstall-${process.cwd()}`,
|
|
])
|
|
})
|
|
|
|
test('run install scripts in the current project when its name is different than its directory', async () => {
|
|
await using server = await createTestIpcServer()
|
|
prepareEmpty()
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage({
|
|
name: 'different-name',
|
|
scripts: {
|
|
install: `node -e "console.log('install-' + process.cwd())" | ${server.generateSendStdinScript()}`,
|
|
postinstall: `node -e "console.log('postinstall-' + process.cwd())" | ${server.generateSendStdinScript()}`,
|
|
preinstall: `node -e "console.log('preinstall-' + process.cwd())" | ${server.generateSendStdinScript()}`,
|
|
},
|
|
}, [], testDefaults({ fastUnpack: false }))
|
|
await install(manifest, testDefaults({ fastUnpack: false }))
|
|
|
|
expect(server.getLines()).toStrictEqual([
|
|
`preinstall-${process.cwd()}`,
|
|
`install-${process.cwd()}`,
|
|
`postinstall-${process.cwd()}`,
|
|
])
|
|
})
|
|
|
|
test('installation fails if lifecycle script fails', async () => {
|
|
prepareEmpty()
|
|
|
|
await expect(
|
|
install({
|
|
scripts: {
|
|
preinstall: 'exit 1',
|
|
},
|
|
}, testDefaults({ fastUnpack: false }))
|
|
).rejects.toThrow(/@ preinstall: `exit 1`/)
|
|
})
|
|
|
|
test('INIT_CWD is always set to lockfile directory', async () => {
|
|
prepareEmpty()
|
|
const rootDir = process.cwd() as ProjectRootDir
|
|
fs.mkdirSync('sub_dir')
|
|
process.chdir('sub_dir')
|
|
await mutateModulesInSingleProject({
|
|
mutation: 'install',
|
|
manifest: {
|
|
dependencies: {
|
|
'@pnpm.e2e/write-lifecycle-env': '1.0.0',
|
|
},
|
|
scripts: {
|
|
install: 'node -e "fs.writeFileSync(\'output.json\', JSON.stringify(process.env.INIT_CWD))"',
|
|
},
|
|
},
|
|
rootDir,
|
|
}, testDefaults({
|
|
fastUnpack: false,
|
|
lockfileDir: rootDir,
|
|
allowBuilds: { '@pnpm.e2e/write-lifecycle-env': true },
|
|
}))
|
|
|
|
const childEnv = loadJsonFileSync<{ INIT_CWD: string }>(path.join(rootDir, 'node_modules/@pnpm.e2e/write-lifecycle-env/env.json'))
|
|
expect(childEnv.INIT_CWD).toBe(rootDir)
|
|
|
|
const output = loadJsonFileSync(path.join(rootDir, 'output.json'))
|
|
expect(output).toStrictEqual(process.cwd())
|
|
})
|
|
|
|
// TODO: duplicate this test to @pnpm/lifecycle
|
|
test("reports child's output", async () => {
|
|
prepareEmpty()
|
|
|
|
const reporter = jest.fn()
|
|
|
|
await addDependenciesToPackage({}, ['@pnpm.e2e/count-to-10'], testDefaults({ fastUnpack: false, reporter, allowBuilds: { '@pnpm.e2e/count-to-10': true } }))
|
|
|
|
expect(reporter).toHaveBeenCalledWith(expect.objectContaining({
|
|
depPath: '@pnpm.e2e/count-to-10@1.0.0',
|
|
level: 'debug',
|
|
name: 'pnpm:lifecycle',
|
|
script: 'node postinstall',
|
|
stage: 'postinstall',
|
|
} as LifecycleLog))
|
|
expect(reporter).toHaveBeenCalledWith(expect.objectContaining({
|
|
depPath: '@pnpm.e2e/count-to-10@1.0.0',
|
|
level: 'debug',
|
|
line: '1',
|
|
name: 'pnpm:lifecycle',
|
|
stage: 'postinstall',
|
|
stdio: 'stdout',
|
|
} as LifecycleLog))
|
|
expect(reporter).toHaveBeenCalledWith(expect.objectContaining({
|
|
depPath: '@pnpm.e2e/count-to-10@1.0.0',
|
|
level: 'debug',
|
|
line: '2',
|
|
name: 'pnpm:lifecycle',
|
|
stage: 'postinstall',
|
|
stdio: 'stdout',
|
|
} as LifecycleLog))
|
|
expect(reporter).toHaveBeenCalledWith(expect.objectContaining({
|
|
depPath: '@pnpm.e2e/count-to-10@1.0.0',
|
|
level: 'debug',
|
|
line: '6',
|
|
name: 'pnpm:lifecycle',
|
|
stage: 'postinstall',
|
|
stdio: 'stderr',
|
|
} as LifecycleLog))
|
|
expect(reporter).toHaveBeenCalledWith(expect.objectContaining({
|
|
depPath: '@pnpm.e2e/count-to-10@1.0.0',
|
|
exitCode: 0,
|
|
level: 'debug',
|
|
name: 'pnpm:lifecycle',
|
|
stage: 'postinstall',
|
|
} as LifecycleLog))
|
|
})
|
|
|
|
test("reports child's close event", async () => {
|
|
prepareEmpty()
|
|
|
|
const reporter = jest.fn()
|
|
|
|
await expect(
|
|
addDependenciesToPackage({}, ['@pnpm.e2e/failing-postinstall'], testDefaults({ reporter, allowBuilds: { '@pnpm.e2e/failing-postinstall': true } }))
|
|
).rejects.toThrow()
|
|
|
|
expect(reporter).toHaveBeenCalledWith(expect.objectContaining({
|
|
depPath: '@pnpm.e2e/failing-postinstall@1.0.0',
|
|
exitCode: 1,
|
|
level: 'debug',
|
|
name: 'pnpm:lifecycle',
|
|
stage: 'postinstall',
|
|
} as LifecycleLog))
|
|
})
|
|
|
|
testOnNonWindows('lifecycle scripts have access to node-gyp', async () => {
|
|
prepareEmpty()
|
|
|
|
// `npm test` adds node-gyp to the PATH
|
|
// it is removed here to test that pnpm adds it
|
|
const initialPath = process.env[PATH]
|
|
|
|
if (typeof initialPath !== 'string') throw new Error('PATH is not defined')
|
|
|
|
process.env[PATH] = initialPath
|
|
.split(path.delimiter)
|
|
.filter((p: string) => !p.includes('node-gyp-bin') &&
|
|
!p.includes(`${path.sep}npm${path.sep}`) &&
|
|
!p.includes(`${path.sep}.npm${path.sep}`))
|
|
.join(path.delimiter)
|
|
|
|
await addDependenciesToPackage({}, ['drivelist@5.1.8'], testDefaults({ fastUnpack: false, allowBuilds: { drivelist: true } }))
|
|
|
|
process.env[PATH] = initialPath
|
|
})
|
|
|
|
test('run lifecycle scripts of dependent packages after running scripts of their deps', async () => {
|
|
const project = prepareEmpty()
|
|
|
|
await addDependenciesToPackage({}, ['@pnpm.e2e/with-postinstall-a'], testDefaults({ fastUnpack: false, allowBuilds: { '@pnpm.e2e/with-postinstall-a': true, '@pnpm.e2e/with-postinstall-b': true } }))
|
|
|
|
expect(+project.requireModule('.pnpm/@pnpm.e2e+with-postinstall-b@1.0.0/node_modules/@pnpm.e2e/with-postinstall-b/output.json')[0] < +project.requireModule('@pnpm.e2e/with-postinstall-a/output.json')[0]).toBeTruthy()
|
|
})
|
|
|
|
test('run prepare script for git-hosted dependencies', async () => {
|
|
const project = prepareEmpty()
|
|
|
|
await addDependenciesToPackage({}, ['pnpm/test-git-fetch#8b333f12d5357f4f25a654c305c826294cb073bf'], testDefaults({
|
|
fastUnpack: false,
|
|
allowBuilds: { 'test-git-fetch': true },
|
|
neverBuiltDependencies: undefined,
|
|
}))
|
|
|
|
const scripts = project.requireModule('test-git-fetch/output.json')
|
|
expect(scripts).toStrictEqual([
|
|
'preinstall',
|
|
'install',
|
|
'postinstall',
|
|
'prepare',
|
|
'preinstall',
|
|
'install',
|
|
'postinstall',
|
|
])
|
|
})
|
|
|
|
test('lifecycle scripts run before linking bins', async () => {
|
|
const project = prepareEmpty()
|
|
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage({}, ['@pnpm.e2e/generated-bins'], testDefaults({ fastUnpack: false, allowBuilds: { '@pnpm.e2e/generated-bins': true } }))
|
|
|
|
project.isExecutable('.bin/cmd1')
|
|
project.isExecutable('.bin/cmd2')
|
|
|
|
rimrafSync('node_modules')
|
|
|
|
await mutateModulesInSingleProject({
|
|
manifest,
|
|
mutation: 'install',
|
|
rootDir: process.cwd() as ProjectRootDir,
|
|
}, testDefaults({ frozenLockfile: true, allowBuilds: { '@pnpm.e2e/generated-bins': true } }))
|
|
|
|
project.isExecutable('.bin/cmd1')
|
|
project.isExecutable('.bin/cmd2')
|
|
})
|
|
|
|
test('hoisting does not fail on commands that will be created by lifecycle scripts on a later stage', async () => {
|
|
prepareEmpty()
|
|
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage({}, ['@pnpm.e2e/has-generated-bins-as-dep'], testDefaults({ fastUnpack: false, hoistPattern: '*', allowBuilds: { '@pnpm.e2e/has-generated-bins-as-dep': true, '@pnpm.e2e/generated-bins': true } }))
|
|
|
|
// project.isExecutable('.pnpm/node_modules/.bin/cmd1')
|
|
// project.isExecutable('.pnpm/node_modules/.bin/cmd2')
|
|
|
|
// Testing the same with headless installation
|
|
rimrafSync('node_modules')
|
|
|
|
await mutateModulesInSingleProject({
|
|
manifest,
|
|
mutation: 'install',
|
|
rootDir: process.cwd() as ProjectRootDir,
|
|
}, testDefaults({ frozenLockfile: true, hoistPattern: '*', allowBuilds: { '@pnpm.e2e/has-generated-bins-as-dep': true, '@pnpm.e2e/generated-bins': true } }))
|
|
|
|
// project.isExecutable('.pnpm/node_modules/.bin/cmd1')
|
|
// project.isExecutable('.pnpm/node_modules/.bin/cmd2')
|
|
})
|
|
|
|
test('bins are linked even if lifecycle scripts are ignored', async () => {
|
|
const project = prepareEmpty()
|
|
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage(
|
|
{},
|
|
[
|
|
'@pnpm.e2e/pkg-with-peer-having-bin',
|
|
'@pnpm.e2e/peer-with-bin',
|
|
'@pnpm.e2e/pre-and-postinstall-scripts-example@1.0.0',
|
|
],
|
|
testDefaults({ fastUnpack: false, ignoreScripts: true })
|
|
)
|
|
|
|
project.isExecutable('.bin/peer-with-bin')
|
|
project.isExecutable('@pnpm.e2e/pkg-with-peer-having-bin/node_modules/.bin/hello-world-js-bin')
|
|
|
|
// Verifying that the scripts were ignored
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/package.json')).toBeTruthy()
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js')).toBeFalsy()
|
|
|
|
rimrafSync('node_modules')
|
|
|
|
await mutateModulesInSingleProject({
|
|
manifest,
|
|
mutation: 'install',
|
|
rootDir: process.cwd() as ProjectRootDir,
|
|
}, testDefaults({ frozenLockfile: true, ignoreScripts: true }))
|
|
|
|
project.isExecutable('.bin/peer-with-bin')
|
|
project.isExecutable('@pnpm.e2e/pkg-with-peer-having-bin/node_modules/.bin/hello-world-js-bin')
|
|
|
|
// Verifying that the scripts were ignored
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/package.json')).toBeTruthy()
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js')).toBeFalsy()
|
|
})
|
|
|
|
test('dependency should not be added to current lockfile if it was not built successfully during headless install', async () => {
|
|
const project = prepareEmpty()
|
|
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage(
|
|
{},
|
|
[
|
|
'package-that-cannot-be-installed@0.0.0', // TODO: this package should be replaced
|
|
],
|
|
testDefaults({
|
|
ignoreScripts: true,
|
|
lockfileOnly: true,
|
|
})
|
|
)
|
|
|
|
await expect(
|
|
mutateModulesInSingleProject({
|
|
manifest,
|
|
mutation: 'install',
|
|
rootDir: process.cwd() as ProjectRootDir,
|
|
}, testDefaults({ frozenLockfile: true, allowBuilds: { 'package-that-cannot-be-installed': true } }))
|
|
).rejects.toThrow()
|
|
|
|
expect(project.readCurrentLockfile()).toBeFalsy()
|
|
})
|
|
|
|
test('scripts have access to unlisted bins when hoisting is used', async () => {
|
|
const project = prepareEmpty()
|
|
|
|
await addDependenciesToPackage(
|
|
{},
|
|
['@pnpm.e2e/pkg-that-calls-unlisted-dep-in-hooks'],
|
|
testDefaults({ fastUnpack: false, hoistPattern: '*', allowBuilds: { '@pnpm.e2e/pkg-that-calls-unlisted-dep-in-hooks': true } })
|
|
)
|
|
|
|
expect(project.requireModule('@pnpm.e2e/pkg-that-calls-unlisted-dep-in-hooks/output.json')).toStrictEqual(['Hello world!'])
|
|
})
|
|
|
|
test('selectively ignore scripts in some dependencies by allowBuilds (not others)', async () => {
|
|
prepareEmpty()
|
|
const allowBuilds = { '@pnpm.e2e/install-script-example': true }
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage({},
|
|
['@pnpm.e2e/pre-and-postinstall-scripts-example@1.0.0', '@pnpm.e2e/install-script-example'],
|
|
testDefaults({ fastUnpack: false, allowBuilds })
|
|
)
|
|
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js')).toBeFalsy()
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall.js')).toBeFalsy()
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/install-script-example/generated-by-install.js')).toBeTruthy()
|
|
|
|
rimrafSync('node_modules')
|
|
|
|
await install(manifest, testDefaults({ fastUnpack: false, frozenLockfile: true, allowBuilds }))
|
|
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js')).toBeFalsy()
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall.js')).toBeFalsy()
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/install-script-example/generated-by-install.js')).toBeTruthy()
|
|
})
|
|
|
|
test('selectively allow scripts in some dependencies by allowBuilds', async () => {
|
|
prepareEmpty()
|
|
const reporter = jest.fn()
|
|
const allowBuilds = { '@pnpm.e2e/install-script-example': true }
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage({},
|
|
['@pnpm.e2e/pre-and-postinstall-scripts-example@1.0.0', '@pnpm.e2e/install-script-example'],
|
|
testDefaults({ fastUnpack: false, allowBuilds, reporter })
|
|
)
|
|
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js')).toBeFalsy()
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall.js')).toBeFalsy()
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/install-script-example/generated-by-install.js')).toBeTruthy()
|
|
|
|
{
|
|
const ignoredPkgsLog = reporter.mock.calls.find((call) => (call[0] as Record<string, unknown>).name === 'pnpm:ignored-scripts')![0] as Record<string, unknown>
|
|
expect(ignoredPkgsLog.packageNames).toStrictEqual(['@pnpm.e2e/pre-and-postinstall-scripts-example@1.0.0'])
|
|
}
|
|
reporter.mockClear()
|
|
|
|
rimrafSync('node_modules')
|
|
|
|
await install(manifest, testDefaults({
|
|
fastUnpack: false,
|
|
frozenLockfile: true,
|
|
allowBuilds: { ...allowBuilds, '@pnpm.e2e/pre-and-postinstall-scripts-example': false },
|
|
reporter,
|
|
}))
|
|
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js')).toBeFalsy()
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall.js')).toBeFalsy()
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/install-script-example/generated-by-install.js')).toBeTruthy()
|
|
|
|
{
|
|
const ignoredPkgsLog = reporter.mock.calls.find((call) => (call[0] as Record<string, unknown>).name === 'pnpm:ignored-scripts')![0] as Record<string, unknown>
|
|
expect(ignoredPkgsLog.packageNames).toStrictEqual([])
|
|
}
|
|
})
|
|
|
|
test('selectively allow scripts in some dependencies by allowBuilds using exact versions', async () => {
|
|
prepareEmpty()
|
|
const reporter = jest.fn()
|
|
const allowBuilds = { '@pnpm.e2e/install-script-example@1.0.0': true }
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage({},
|
|
['@pnpm.e2e/pre-and-postinstall-scripts-example@1.0.0', '@pnpm.e2e/install-script-example'],
|
|
testDefaults({ fastUnpack: false, allowBuilds, reporter })
|
|
)
|
|
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js')).toBeFalsy()
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall.js')).toBeFalsy()
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/install-script-example/generated-by-install.js')).toBeTruthy()
|
|
|
|
{
|
|
const ignoredPkgsLog = reporter.mock.calls.find((call) => (call[0] as Record<string, unknown>).name === 'pnpm:ignored-scripts')![0] as Record<string, unknown>
|
|
expect(ignoredPkgsLog.packageNames).toStrictEqual(['@pnpm.e2e/pre-and-postinstall-scripts-example@1.0.0'])
|
|
}
|
|
reporter.mockClear()
|
|
|
|
rimrafSync('node_modules')
|
|
|
|
await install(manifest, testDefaults({
|
|
fastUnpack: false,
|
|
frozenLockfile: true,
|
|
allowBuilds: { ...allowBuilds, '@pnpm.e2e/pre-and-postinstall-scripts-example': false },
|
|
reporter,
|
|
}))
|
|
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js')).toBeFalsy()
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall.js')).toBeFalsy()
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/install-script-example/generated-by-install.js')).toBeTruthy()
|
|
|
|
{
|
|
const ignoredPkgsLog = reporter.mock.calls.find((call) => (call[0] as Record<string, unknown>).name === 'pnpm:ignored-scripts')![0] as Record<string, unknown>
|
|
expect(ignoredPkgsLog.packageNames).toStrictEqual([])
|
|
}
|
|
})
|
|
|
|
test('lifecycle scripts have access to package\'s own binary by binary name', async () => {
|
|
const project = prepareEmpty()
|
|
await addDependenciesToPackage({},
|
|
['@pnpm.e2e/runs-own-bin'],
|
|
testDefaults({ fastUnpack: false, allowBuilds: { '@pnpm.e2e/runs-own-bin': true } })
|
|
)
|
|
|
|
project.isExecutable('.pnpm/@pnpm.e2e+runs-own-bin@1.0.0/node_modules/@pnpm.e2e/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',
|
|
'@pnpm.e2e/postinstall-requires-is-positive': '1.0.0',
|
|
},
|
|
}
|
|
|
|
await mutateModulesInSingleProject({
|
|
manifest,
|
|
mutation: 'install',
|
|
rootDir: process.cwd() as ProjectRootDir,
|
|
}, testDefaults({ fastUnpack: false, allowBuilds: { '@pnpm.e2e/postinstall-requires-is-positive': true } }))
|
|
|
|
rimrafSync('node_modules')
|
|
|
|
await mutateModulesInSingleProject({
|
|
manifest,
|
|
mutation: 'install',
|
|
rootDir: process.cwd() as ProjectRootDir,
|
|
}, testDefaults({ fastUnpack: false, frozenLockfile: true, allowBuilds: { '@pnpm.e2e/postinstall-requires-is-positive': true } }))
|
|
|
|
// if there was no exception, the test passed
|
|
})
|
|
|
|
test('ignore-dep-scripts', async () => {
|
|
await using server1 = await createTestIpcServer()
|
|
await using server2 = await createTestIpcServer()
|
|
prepareEmpty()
|
|
const manifest = {
|
|
scripts: {
|
|
'pnpm:devPreinstall': server2.sendLineScript('pnpm:devPreinstall'),
|
|
install: server1.sendLineScript('install'),
|
|
postinstall: server1.sendLineScript('postinstall'),
|
|
preinstall: server1.sendLineScript('preinstall'),
|
|
},
|
|
dependencies: {
|
|
'@pnpm.e2e/pre-and-postinstall-scripts-example': '1.0.0',
|
|
},
|
|
}
|
|
await install(manifest, testDefaults({ fastUnpack: false, ignoreDepScripts: true }))
|
|
|
|
expect(server1.getLines()).toStrictEqual(['preinstall', 'install', 'postinstall'])
|
|
expect(server2.getLines()).toStrictEqual(['pnpm:devPreinstall'])
|
|
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js')).toBeFalsy()
|
|
|
|
rimrafSync('node_modules')
|
|
server1.clear()
|
|
server2.clear()
|
|
await install(manifest, testDefaults({ fastUnpack: false, ignoreDepScripts: true }))
|
|
|
|
expect(server1.getLines()).toStrictEqual(['preinstall', 'install', 'postinstall'])
|
|
expect(server2.getLines()).toStrictEqual(['pnpm:devPreinstall'])
|
|
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js')).toBeFalsy()
|
|
})
|
|
|
|
test('run pre/postinstall scripts in a workspace that uses node-linker=hoisted', async () => {
|
|
await restartWorkerPool()
|
|
const projects = preparePackages([
|
|
{
|
|
location: 'project-1',
|
|
package: { name: 'project-1' },
|
|
},
|
|
{
|
|
location: 'project-2',
|
|
package: { name: 'project-2' },
|
|
},
|
|
{
|
|
location: 'project-3',
|
|
package: { name: 'project-3' },
|
|
},
|
|
{
|
|
location: 'project-4',
|
|
package: { name: 'project-4' },
|
|
},
|
|
])
|
|
const importers: MutatedProject[] = [
|
|
{
|
|
mutation: 'install',
|
|
rootDir: path.resolve('project-1') as ProjectRootDir,
|
|
},
|
|
{
|
|
mutation: 'install',
|
|
rootDir: path.resolve('project-2') as ProjectRootDir,
|
|
},
|
|
{
|
|
mutation: 'install',
|
|
rootDir: path.resolve('project-3') as ProjectRootDir,
|
|
},
|
|
{
|
|
mutation: 'install',
|
|
rootDir: path.resolve('project-4') as ProjectRootDir,
|
|
},
|
|
]
|
|
const allProjects = [
|
|
{
|
|
buildIndex: 0,
|
|
manifest: {
|
|
name: 'project-1',
|
|
version: '1.0.0',
|
|
|
|
dependencies: {
|
|
'@pnpm.e2e/pre-and-postinstall-scripts-example': '1',
|
|
},
|
|
},
|
|
rootDir: path.resolve('project-1') as ProjectRootDir,
|
|
},
|
|
{
|
|
buildIndex: 0,
|
|
manifest: {
|
|
name: 'project-2',
|
|
version: '1.0.0',
|
|
|
|
dependencies: {
|
|
'@pnpm.e2e/pre-and-postinstall-scripts-example': '1',
|
|
},
|
|
},
|
|
rootDir: path.resolve('project-2') as ProjectRootDir,
|
|
},
|
|
{
|
|
buildIndex: 0,
|
|
manifest: {
|
|
name: 'project-3',
|
|
version: '1.0.0',
|
|
|
|
dependencies: {
|
|
'@pnpm.e2e/pre-and-postinstall-scripts-example': '2',
|
|
},
|
|
},
|
|
rootDir: path.resolve('project-3') as ProjectRootDir,
|
|
},
|
|
{
|
|
buildIndex: 0,
|
|
manifest: {
|
|
name: 'project-4',
|
|
version: '1.0.0',
|
|
|
|
dependencies: {
|
|
'@pnpm.e2e/pre-and-postinstall-scripts-example': '2',
|
|
},
|
|
},
|
|
rootDir: path.resolve('project-4') as ProjectRootDir,
|
|
},
|
|
]
|
|
await mutateModules(importers, testDefaults({
|
|
allProjects,
|
|
fastUnpack: false,
|
|
nodeLinker: 'hoisted',
|
|
allowBuilds: { '@pnpm.e2e/pre-and-postinstall-scripts-example': true },
|
|
}))
|
|
const rootProject = assertProject(process.cwd())
|
|
rootProject.has('@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js')
|
|
rootProject.has('@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall.js')
|
|
projects['project-1'].hasNot('@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js')
|
|
projects['project-1'].hasNot('@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall.js')
|
|
projects['project-2'].hasNot('@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js')
|
|
projects['project-2'].hasNot('@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall.js')
|
|
projects['project-3'].has('@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js')
|
|
projects['project-3'].has('@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall.js')
|
|
projects['project-4'].has('@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js')
|
|
projects['project-4'].has('@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall.js')
|
|
})
|
|
|
|
test('run pre/postinstall scripts in a project that uses node-linker=hoisted. Should not fail on repeat install', async () => {
|
|
const project = prepareEmpty()
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage({},
|
|
['@pnpm.e2e/pre-and-postinstall-scripts-example@1.0.0'],
|
|
testDefaults({ fastUnpack: false, targetDependenciesField: 'devDependencies', nodeLinker: 'hoisted', sideEffectsCacheRead: true, sideEffectsCacheWrite: true, allowBuilds: { '@pnpm.e2e/pre-and-postinstall-scripts-example': true } })
|
|
)
|
|
|
|
{
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-prepare.js')).toBeFalsy()
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js')).toBeTruthy()
|
|
|
|
const generatedByPreinstall = project.requireModule('@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall')
|
|
expect(typeof generatedByPreinstall).toBe('function')
|
|
|
|
const generatedByPostinstall = project.requireModule('@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall')
|
|
expect(typeof generatedByPostinstall).toBe('function')
|
|
}
|
|
|
|
const reporter = jest.fn()
|
|
await addDependenciesToPackage(manifest,
|
|
['example@npm:@pnpm.e2e/pre-and-postinstall-scripts-example@2.0.0'],
|
|
testDefaults({
|
|
fastUnpack: false,
|
|
targetDependenciesField: 'devDependencies',
|
|
nodeLinker: 'hoisted',
|
|
reporter,
|
|
sideEffectsCacheRead: true,
|
|
sideEffectsCacheWrite: true,
|
|
allowBuilds: { '@pnpm.e2e/pre-and-postinstall-scripts-example': true },
|
|
})
|
|
)
|
|
|
|
expect(reporter).not.toHaveBeenCalledWith(expect.objectContaining({
|
|
level: 'warn',
|
|
message: `An error occurred while uploading ${path.resolve('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example')}`,
|
|
}))
|
|
})
|
|
|
|
test('build dependencies that were not previously built after allowBuilds changes', async () => {
|
|
prepareEmpty()
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage({},
|
|
['@pnpm.e2e/pre-and-postinstall-scripts-example@1.0.0', '@pnpm.e2e/install-script-example'],
|
|
testDefaults({
|
|
fastUnpack: false,
|
|
allowBuilds: { '@pnpm.e2e/install-script-example': true },
|
|
})
|
|
)
|
|
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js')).toBeFalsy()
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall.js')).toBeFalsy()
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/install-script-example/generated-by-install.js')).toBeTruthy()
|
|
|
|
await install(manifest, testDefaults({
|
|
fastUnpack: false,
|
|
frozenLockfile: true,
|
|
allowBuilds: { '@pnpm.e2e/install-script-example': true, '@pnpm.e2e/pre-and-postinstall-scripts-example@1.0.0': true },
|
|
}))
|
|
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js')).toBeTruthy()
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall.js')).toBeTruthy()
|
|
expect(fs.existsSync('node_modules/@pnpm.e2e/install-script-example/generated-by-install.js')).toBeTruthy()
|
|
})
|