mirror of
https://github.com/pnpm/pnpm.git
synced 2025-12-24 07:38:12 -05:00
5
.changeset/chilled-badgers-sing.md
Normal file
5
.changeset/chilled-badgers-sing.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/config": minor
|
||||
---
|
||||
|
||||
New option added: `node-linker`. When `node-linker` is set to `pnp`, pnpm will create a `.pnp.js` file.
|
||||
6
.changeset/eight-cobras-pretend.md
Normal file
6
.changeset/eight-cobras-pretend.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@pnpm/build-modules": minor
|
||||
"@pnpm/lifecycle": minor
|
||||
---
|
||||
|
||||
New option added: extraEnv. extraEnv allows to pass environment variables that will be set for the child process.
|
||||
5
.changeset/famous-lions-shake.md
Normal file
5
.changeset/famous-lions-shake.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/lifecycle": minor
|
||||
---
|
||||
|
||||
New function exported: `makeNodeRequireOption()`.
|
||||
6
.changeset/loud-olives-visit.md
Normal file
6
.changeset/loud-olives-visit.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@pnpm/headless": minor
|
||||
"supi": patch
|
||||
---
|
||||
|
||||
New option added: `enablePnp`. When enablePnp is true, a `.pnp.js` file is generated.
|
||||
5
.changeset/perfect-rivers-switch.md
Normal file
5
.changeset/perfect-rivers-switch.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/plugin-commands-script-runners": minor
|
||||
---
|
||||
|
||||
Scripts support Plug'n'Play.
|
||||
5
.changeset/ten-rules-cough.md
Normal file
5
.changeset/ten-rules-cough.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/lockfile-to-pnp": minor
|
||||
---
|
||||
|
||||
CLI command removed.
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,6 +6,7 @@ npm-debug.log*
|
||||
# Dependency directory
|
||||
**/node_modules/**
|
||||
_node_modules
|
||||
.pnp.js
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
@@ -18,6 +18,7 @@ export default async (
|
||||
childConcurrency?: number
|
||||
depsToBuild?: Set<string>
|
||||
extraBinPaths?: string[]
|
||||
extraEnv?: Record<string, string>
|
||||
lockfileDir: string
|
||||
optional: boolean
|
||||
rawConfig: object
|
||||
@@ -63,6 +64,7 @@ async function buildDependency (
|
||||
depGraph: DependenciesGraph,
|
||||
opts: {
|
||||
extraBinPaths?: string[]
|
||||
extraEnv?: Record<string, string>
|
||||
lockfileDir: string
|
||||
optional: boolean
|
||||
rawConfig: object
|
||||
@@ -79,6 +81,7 @@ async function buildDependency (
|
||||
const hasSideEffects = await runPostinstallHooks({
|
||||
depPath,
|
||||
extraBinPaths: opts.extraBinPaths,
|
||||
extraEnv: opts.extraEnv,
|
||||
initCwd: opts.lockfileDir,
|
||||
optional: depNode.optional,
|
||||
pkgRoot: depNode.dir,
|
||||
|
||||
@@ -114,6 +114,7 @@ export interface Config {
|
||||
publishBranch?: string
|
||||
recursiveInstall?: boolean
|
||||
symlink: boolean
|
||||
enablePnp?: boolean
|
||||
|
||||
registries: Registries
|
||||
ignoreWorkspaceRootCheck: boolean
|
||||
|
||||
@@ -51,6 +51,7 @@ export const types = Object.assign({
|
||||
loglevel: ['silent', 'error', 'warn', 'info', 'debug'],
|
||||
'modules-dir': String,
|
||||
'network-concurrency': Number,
|
||||
'node-linker': ['pnp'],
|
||||
'npm-path': String,
|
||||
offline: Boolean,
|
||||
'package-import-method': ['auto', 'hardlink', 'clone', 'copy'],
|
||||
@@ -376,6 +377,7 @@ export default async (
|
||||
if (!pnpmConfig.noProxy) {
|
||||
pnpmConfig.noProxy = getProcessEnv('no_proxy')
|
||||
}
|
||||
pnpmConfig.enablePnp = pnpmConfig['nodeLinker'] === 'pnp'
|
||||
return { config: pnpmConfig, warnings }
|
||||
}
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
"@pnpm/lifecycle": "workspace:9.4.0",
|
||||
"@pnpm/link-bins": "workspace:5.3.14",
|
||||
"@pnpm/lockfile-file": "workspace:3.0.14",
|
||||
"@pnpm/lockfile-to-pnp": "workspace:^0.2.0",
|
||||
"@pnpm/lockfile-utils": "workspace:2.0.16",
|
||||
"@pnpm/modules-cleaner": "workspace:10.0.11",
|
||||
"@pnpm/modules-yaml": "workspace:8.0.2",
|
||||
|
||||
@@ -17,7 +17,10 @@ import {
|
||||
filterLockfileByImportersAndEngine,
|
||||
} from '@pnpm/filter-lockfile'
|
||||
import hoist from '@pnpm/hoist'
|
||||
import { runLifecycleHooksConcurrently } from '@pnpm/lifecycle'
|
||||
import {
|
||||
runLifecycleHooksConcurrently,
|
||||
makeNodeRequireOption,
|
||||
} from '@pnpm/lifecycle'
|
||||
import linkBins, { linkBinsOfPackages } from '@pnpm/link-bins'
|
||||
import {
|
||||
getLockfileImporterId,
|
||||
@@ -27,6 +30,7 @@ import {
|
||||
readWantedLockfile,
|
||||
writeCurrentLockfile,
|
||||
} from '@pnpm/lockfile-file'
|
||||
import { writePnpFile } from '@pnpm/lockfile-to-pnp'
|
||||
import {
|
||||
nameVerFromPkgSnapshot,
|
||||
packageIdFromSnapshot,
|
||||
@@ -70,6 +74,7 @@ export interface HeadlessOptions {
|
||||
nodeVersion: string
|
||||
pnpmVersion: string
|
||||
}
|
||||
enablePnp?: boolean
|
||||
engineStrict: boolean
|
||||
extraBinPaths?: string[]
|
||||
ignoreScripts: boolean
|
||||
@@ -211,6 +216,17 @@ export default async (opts: HeadlessOptions) => {
|
||||
virtualStoreDir,
|
||||
} as LockfileToDepGraphOptions
|
||||
)
|
||||
if (opts.enablePnp) {
|
||||
const importerNames = R.fromPairs(
|
||||
opts.projects.map(({ manifest, id }) => [id, manifest.name ?? id])
|
||||
)
|
||||
await writePnpFile(filteredLockfile, {
|
||||
importerNames,
|
||||
lockfileDir,
|
||||
virtualStoreDir,
|
||||
registries: opts.registries,
|
||||
})
|
||||
}
|
||||
const depNodes = R.values(graph)
|
||||
|
||||
statsLogger.debug({
|
||||
@@ -313,9 +329,14 @@ export default async (opts: HeadlessOptions) => {
|
||||
if (opts.hoistPattern) {
|
||||
extraBinPaths.unshift(path.join(virtualStoreDir, 'node_modules/.bin'))
|
||||
}
|
||||
let extraEnv: Record<string, string> | undefined
|
||||
if (opts.enablePnp) {
|
||||
extraEnv = makeNodeRequireOption(path.join(opts.lockfileDir, '.pnp.js'))
|
||||
}
|
||||
await buildModules(graph, Array.from(directNodes), {
|
||||
childConcurrency: opts.childConcurrency,
|
||||
extraBinPaths,
|
||||
extraEnv,
|
||||
lockfileDir,
|
||||
optional: opts.include.optionalDependencies,
|
||||
rawConfig: opts.rawConfig,
|
||||
|
||||
@@ -752,11 +752,13 @@ test('installing in a workspace', async (t) => {
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('installing with no symlinks', async (t) => {
|
||||
test('installing with no symlinks but with PnP', async (t) => {
|
||||
const prefix = path.join(fixtures, 'simple')
|
||||
await rimraf(path.join(prefix, 'node_modules'))
|
||||
await rimraf(path.join(prefix, '.pnp.js'))
|
||||
|
||||
await headless(await testDefaults({
|
||||
enablePnp: true,
|
||||
lockfileDir: prefix,
|
||||
symlink: false,
|
||||
}))
|
||||
@@ -767,6 +769,7 @@ test('installing with no symlinks', async (t) => {
|
||||
const project = assertProject(t, prefix)
|
||||
t.ok(await project.readCurrentLockfile())
|
||||
t.ok(await project.readModulesManifest())
|
||||
t.ok(await exists(path.join(prefix, '.pnp.js')))
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
{
|
||||
"path": "../lockfile-file"
|
||||
},
|
||||
{
|
||||
"path": "../lockfile-to-pnp"
|
||||
},
|
||||
{
|
||||
"path": "../lockfile-utils"
|
||||
},
|
||||
|
||||
@@ -4,6 +4,12 @@ import runLifecycleHooksConcurrently, { RunLifecycleHooksConcurrentlyOptions } f
|
||||
import path = require('path')
|
||||
import exists = require('path-exists')
|
||||
|
||||
export function makeNodeRequireOption (modulePath: string) {
|
||||
let { NODE_OPTIONS } = process.env
|
||||
NODE_OPTIONS = `${NODE_OPTIONS ?? ''} --require=${modulePath}`.trim()
|
||||
return { NODE_OPTIONS }
|
||||
}
|
||||
|
||||
export default runLifecycleHook
|
||||
export {
|
||||
runLifecycleHooksConcurrently,
|
||||
|
||||
@@ -8,6 +8,7 @@ export interface RunLifecycleHookOptions {
|
||||
args?: string[]
|
||||
depPath: string
|
||||
extraBinPaths?: string[]
|
||||
extraEnv?: Record<string, string>
|
||||
initCwd?: string
|
||||
optional?: boolean
|
||||
pkgRoot: string
|
||||
@@ -54,6 +55,7 @@ export default async function runLifecycleHook (
|
||||
dir: opts.rootModulesDir,
|
||||
extraBinPaths: opts.extraBinPaths ?? [],
|
||||
extraEnv: {
|
||||
...opts.extraEnv,
|
||||
INIT_CWD: opts.initCwd ?? process.cwd(),
|
||||
PNPM_SCRIPT_SRC_DIR: opts.pkgRoot,
|
||||
},
|
||||
|
||||
@@ -10,14 +10,6 @@
|
||||
pnpm add -g @pnpm/lockfile-to-pnp
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
1. Run `pnpm install` to create an up-to-date lockfile and virtual store.
|
||||
1. Run `lockfile-to-pnp` in the directory that has a lockfile.
|
||||
1. When executing a Node script, use the generated `.pnp.js` file to hook Node's resolver.
|
||||
|
||||
E.g., if to run `index.js`, use this command: `node --require ./.pnp.js index.js`
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
"description": "Creates a Plug'n'Play file from a pnpm-lock.yaml",
|
||||
"main": "lib/index.js",
|
||||
"typings": "lib/index.d.ts",
|
||||
"bin": "lib/createPnpBin.js",
|
||||
"engines": {
|
||||
"node": ">=10.16"
|
||||
},
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
import { lockfileToPnp } from '.'
|
||||
|
||||
lockfileToPnp(process.cwd())
|
||||
.then(() => console.log('Created .pnp.js'))
|
||||
.catch((err) => console.error(err))
|
||||
@@ -40,7 +40,7 @@ export async function lockfileToPnp (lockfileDir: string) {
|
||||
export async function writePnpFile (
|
||||
lockfile: Lockfile,
|
||||
opts: {
|
||||
importerNames: { [importerId: string]: string }
|
||||
importerNames: Record<string, string>
|
||||
lockfileDir: string
|
||||
virtualStoreDir: string
|
||||
registries: Registries
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
"@pnpm/sort-packages": "workspace:1.0.13",
|
||||
"@pnpm/types": "workspace:6.2.0",
|
||||
"p-limit": "^3.0.2",
|
||||
"path-exists": "^4.0.0",
|
||||
"ramda": "^0.27.1",
|
||||
"realpath-missing": "^1.0.0",
|
||||
"render-help": "^1.0.0"
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { RecursiveSummary, throwOnCommandFail } from '@pnpm/cli-utils'
|
||||
import { Config, types } from '@pnpm/config'
|
||||
import PnpmError from '@pnpm/error'
|
||||
import { makeNodeRequireOption } from '@pnpm/lifecycle'
|
||||
import logger from '@pnpm/logger'
|
||||
import sortPackages from '@pnpm/sort-packages'
|
||||
import existsInDir from './existsInDir'
|
||||
import {
|
||||
PARALLEL_OPTION_HELP,
|
||||
shorthands as runShorthands,
|
||||
@@ -55,7 +57,7 @@ export async function handler (
|
||||
rawConfig: object
|
||||
sort?: boolean
|
||||
workspaceConcurrency?: number
|
||||
} & Pick<Config, 'recursive'>,
|
||||
} & Pick<Config, 'recursive' | 'workspaceDir'>,
|
||||
params: string[]
|
||||
) {
|
||||
if (!opts.recursive) {
|
||||
@@ -71,15 +73,22 @@ export async function handler (
|
||||
const chunks = opts.sort
|
||||
? sortPackages(opts.selectedProjectsGraph)
|
||||
: [Object.keys(opts.selectedProjectsGraph).sort()]
|
||||
const existsPnp = existsInDir.bind(null, '.pnp.js')
|
||||
const workspacePnpPath = opts.workspaceDir && await existsPnp(opts.workspaceDir)
|
||||
|
||||
for (const chunk of chunks) {
|
||||
await Promise.all(chunk.map((prefix: string) =>
|
||||
limitRun(async () => {
|
||||
try {
|
||||
const pnpPath = workspacePnpPath ?? await existsPnp(prefix)
|
||||
const extraEnv = pnpPath
|
||||
? makeNodeRequireOption(pnpPath)
|
||||
: {}
|
||||
await execa(params[0], params.slice(1), {
|
||||
cwd: prefix,
|
||||
env: {
|
||||
...process.env,
|
||||
...extraEnv,
|
||||
PNPM_PACKAGE_NAME: opts.selectedProjectsGraph[prefix].package.manifest.name,
|
||||
},
|
||||
stdio: 'inherit',
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import path = require('path')
|
||||
import exists = require('path-exists')
|
||||
|
||||
export default async (entityName: string, dir: string) => {
|
||||
const entityPath = path.join(dir, entityName)
|
||||
if (await exists(entityPath)) return entityPath
|
||||
return undefined
|
||||
}
|
||||
@@ -7,8 +7,12 @@ import { CompletionFunc } from '@pnpm/command'
|
||||
import { FILTERING, UNIVERSAL_OPTIONS } from '@pnpm/common-cli-options-help'
|
||||
import { Config, types as allTypes } from '@pnpm/config'
|
||||
import PnpmError from '@pnpm/error'
|
||||
import runLifecycleHooks from '@pnpm/lifecycle'
|
||||
import runLifecycleHooks, {
|
||||
makeNodeRequireOption,
|
||||
RunLifecycleHookOptions,
|
||||
} from '@pnpm/lifecycle'
|
||||
import { ProjectManifest } from '@pnpm/types'
|
||||
import existsInDir from './existsInDir'
|
||||
import runRecursive, { RecursiveRunOpts } from './runRecursive'
|
||||
import path = require('path')
|
||||
import R = require('ramda')
|
||||
@@ -144,7 +148,7 @@ so you may run "pnpm -w ${scriptName}"`,
|
||||
}
|
||||
throw new PnpmError('NO_SCRIPT', `Missing script: ${scriptName}`)
|
||||
}
|
||||
const lifecycleOpts = {
|
||||
const lifecycleOpts: RunLifecycleHookOptions = {
|
||||
depPath: dir,
|
||||
extraBinPaths: opts.extraBinPaths,
|
||||
pkgRoot: dir,
|
||||
@@ -155,6 +159,12 @@ so you may run "pnpm -w ${scriptName}"`,
|
||||
stdio: 'inherit',
|
||||
unsafePerm: true, // when running scripts explicitly, assume that they're trusted.
|
||||
}
|
||||
const existsPnp = existsInDir.bind(null, '.pnp.js')
|
||||
const pnpPath = (opts.workspaceDir && await existsPnp(opts.workspaceDir)) ??
|
||||
await existsPnp(dir)
|
||||
if (pnpPath) {
|
||||
lifecycleOpts.extraEnv = makeNodeRequireOption(pnpPath)
|
||||
}
|
||||
if (manifest.scripts?.[`pre${scriptName}`]) {
|
||||
await runLifecycleHooks(`pre${scriptName}`, manifest, lifecycleOpts)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { RecursiveSummary, throwOnCommandFail } from '@pnpm/cli-utils'
|
||||
import { Config } from '@pnpm/config'
|
||||
import PnpmError from '@pnpm/error'
|
||||
import runLifecycleHooks from '@pnpm/lifecycle'
|
||||
import runLifecycleHooks, {
|
||||
makeNodeRequireOption,
|
||||
RunLifecycleHookOptions,
|
||||
} from '@pnpm/lifecycle'
|
||||
import logger from '@pnpm/logger'
|
||||
import sortPackages from '@pnpm/sort-packages'
|
||||
import existsInDir from './existsInDir'
|
||||
import path = require('path')
|
||||
import pLimit = require('p-limit')
|
||||
import realpathMissing = require('realpath-missing')
|
||||
@@ -41,6 +45,8 @@ export default async (
|
||||
opts.workspaceConcurrency === 1 ||
|
||||
packageChunks.length === 1 && packageChunks[0].length === 1
|
||||
) ? 'inherit' : 'pipe'
|
||||
const existsPnp = existsInDir.bind(null, '.pnp.js')
|
||||
const workspacePnpPath = opts.workspaceDir && await existsPnp(opts.workspaceDir)
|
||||
|
||||
for (const chunk of packageChunks) {
|
||||
await Promise.all(chunk.map((prefix: string) =>
|
||||
@@ -55,7 +61,7 @@ export default async (
|
||||
}
|
||||
hasCommand++
|
||||
try {
|
||||
const lifecycleOpts = {
|
||||
const lifecycleOpts: RunLifecycleHookOptions = {
|
||||
depPath: prefix,
|
||||
extraBinPaths: opts.extraBinPaths,
|
||||
pkgRoot: prefix,
|
||||
@@ -65,6 +71,10 @@ export default async (
|
||||
stdio,
|
||||
unsafePerm: true, // when running scripts explicitly, assume that they're trusted.
|
||||
}
|
||||
const pnpPath = workspacePnpPath ?? await existsPnp(prefix)
|
||||
if (pnpPath) {
|
||||
lifecycleOpts.extraEnv = makeNodeRequireOption(pnpPath)
|
||||
}
|
||||
if (pkg.package.manifest.scripts[`pre${scriptName}`]) {
|
||||
await runLifecycleHooks(`pre${scriptName}`, pkg.package.manifest, lifecycleOpts)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import execa = require('execa')
|
||||
import fs = require('mz/fs')
|
||||
import test = require('tape')
|
||||
|
||||
const pnpmBin = path.join(__dirname, '../../pnpm/bin/pnpm.js')
|
||||
|
||||
test('pnpm recursive exec', async (t) => {
|
||||
preparePackages(t, [
|
||||
{
|
||||
@@ -248,3 +250,73 @@ test('pnpm exec fails without the recursive=true option', async (t) => {
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('pnpm recursive exec works with PnP', async (t) => {
|
||||
preparePackages(t, [
|
||||
{
|
||||
name: 'project-1',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-1\')" | json-append ../output1.json && node -e "process.stdout.write(\'project-1\')" | json-append ../output2.json',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-2',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
'project-1': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-2\')" | json-append ../output1.json',
|
||||
postbuild: 'node -e "process.stdout.write(\'project-2-postbuild\')" | json-append ../output1.json',
|
||||
prebuild: 'node -e "process.stdout.write(\'project-2-prebuild\')" | json-append ../output1.json',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-3',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
'project-1': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-3\')" | json-append ../output2.json',
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const { selectedProjectsGraph } = await readProjects(process.cwd(), [])
|
||||
await execa(pnpmBin, [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
], {
|
||||
env: {
|
||||
NPM_CONFIG_NODE_LINKER: 'pnp',
|
||||
NPM_CONFIG_SYMLINK: 'false',
|
||||
},
|
||||
})
|
||||
await exec.handler({
|
||||
...DEFAULT_OPTS,
|
||||
recursive: true,
|
||||
selectedProjectsGraph,
|
||||
}, ['npm', 'run', 'build'])
|
||||
|
||||
const outputs1 = await import(path.resolve('output1.json')) as string[]
|
||||
const outputs2 = await import(path.resolve('output2.json')) as string[]
|
||||
|
||||
t.deepEqual(outputs1, ['project-1', 'project-2-prebuild', 'project-2', 'project-2-postbuild'])
|
||||
t.deepEqual(outputs2, ['project-1', 'project-3'])
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
@@ -366,3 +366,27 @@ test('if a script is not found but is present in the root, print an info message
|
||||
t.ok(err.hint.includes('But build is present in the root'))
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('scripts work with PnP', async (t) => {
|
||||
prepare(t, {
|
||||
scripts: {
|
||||
foo: 'node -e "process.stdout.write(\'foo\')" | json-append ./output.json',
|
||||
},
|
||||
})
|
||||
|
||||
await execa(pnpmBin, ['add', 'json-append@1'], {
|
||||
env: {
|
||||
NPM_CONFIG_NODE_LINKER: 'pnp',
|
||||
NPM_CONFIG_SYMLINK: 'false',
|
||||
},
|
||||
})
|
||||
await run.handler({
|
||||
dir: process.cwd(),
|
||||
extraBinPaths: [],
|
||||
rawConfig: {},
|
||||
}, ['foo'])
|
||||
|
||||
const scriptsRan = await import(path.resolve('output.json'))
|
||||
t.deepEqual(scriptsRan, ['foo'])
|
||||
t.end()
|
||||
})
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"@pnpm/lifecycle": "workspace:9.4.0",
|
||||
"@pnpm/link-bins": "workspace:5.3.14",
|
||||
"@pnpm/lockfile-file": "workspace:3.0.14",
|
||||
"@pnpm/lockfile-to-pnp": "workspace:^0.2.0",
|
||||
"@pnpm/lockfile-utils": "workspace:2.0.16",
|
||||
"@pnpm/lockfile-walker": "workspace:3.0.4",
|
||||
"@pnpm/manifest-utils": "workspace:1.1.1",
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface StrictInstallOptions {
|
||||
forceSharedLockfile: boolean
|
||||
frozenLockfile: boolean
|
||||
frozenLockfileIfExists: boolean
|
||||
enablePnp: boolean
|
||||
extraBinPaths: string[]
|
||||
useLockfile: boolean
|
||||
linkWorkspacePackagesDepth: number
|
||||
@@ -88,6 +89,7 @@ const defaults = async (opts: InstallOptions) => {
|
||||
return {
|
||||
childConcurrency: 5,
|
||||
depth: 0,
|
||||
enablePnp: false,
|
||||
engineStrict: false,
|
||||
force: false,
|
||||
forceSharedLockfile: false,
|
||||
|
||||
@@ -12,7 +12,9 @@ import PnpmError from '@pnpm/error'
|
||||
import getContext, { PnpmContext, ProjectOptions } from '@pnpm/get-context'
|
||||
import headless from '@pnpm/headless'
|
||||
import {
|
||||
makeNodeRequireOption,
|
||||
runLifecycleHooksConcurrently,
|
||||
RunLifecycleHooksConcurrentlyOptions,
|
||||
} from '@pnpm/lifecycle'
|
||||
import linkBins, { linkBinsOfPackages } from '@pnpm/link-bins'
|
||||
import {
|
||||
@@ -21,6 +23,7 @@ import {
|
||||
writeLockfiles,
|
||||
writeWantedLockfile,
|
||||
} from '@pnpm/lockfile-file'
|
||||
import { writePnpFile } from '@pnpm/lockfile-to-pnp'
|
||||
import logger, { streamParser } from '@pnpm/logger'
|
||||
import { getAllDependenciesFromManifest } from '@pnpm/manifest-utils'
|
||||
import { write as writeModulesYaml } from '@pnpm/modules-yaml'
|
||||
@@ -180,6 +183,7 @@ export async function mutateModules (
|
||||
pnpmVersion: opts.packageManager.name === 'pnpm' ? opts.packageManager.version : '',
|
||||
},
|
||||
currentLockfile: ctx.currentLockfile,
|
||||
enablePnp: opts.enablePnp,
|
||||
engineStrict: opts.engineStrict,
|
||||
extraBinPaths: opts.extraBinPaths,
|
||||
force: opts.force,
|
||||
@@ -232,7 +236,7 @@ export async function mutateModules (
|
||||
const projectsToInstall = [] as ImporterToUpdate[]
|
||||
|
||||
const projectsToBeInstalled = ctx.projects.filter(({ mutation }) => mutation === 'install') as Array<{ buildIndex: number, rootDir: string, manifest: ProjectManifest, modulesDir: string }>
|
||||
const scriptsOpts = {
|
||||
const scriptsOpts: RunLifecycleHooksConcurrentlyOptions = {
|
||||
extraBinPaths: opts.extraBinPaths,
|
||||
rawConfig: opts.rawConfig,
|
||||
shellEmulator: opts.shellEmulator,
|
||||
@@ -402,6 +406,9 @@ export async function mutateModules (
|
||||
})
|
||||
|
||||
if (!opts.ignoreScripts) {
|
||||
if (opts.enablePnp) {
|
||||
scriptsOpts.extraEnv = makeNodeRequireOption(path.join(opts.lockfileDir, '.pnp.js'))
|
||||
}
|
||||
await runLifecycleHooksConcurrently(['install', 'postinstall', 'prepublish', 'prepare'],
|
||||
projectsToBeInstalled,
|
||||
opts.childConcurrency,
|
||||
@@ -682,6 +689,17 @@ async function installInContext (
|
||||
}
|
||||
)
|
||||
await finishLockfileUpdates()
|
||||
if (opts.enablePnp) {
|
||||
const importerNames = R.fromPairs(
|
||||
projects.map(({ manifest, id }) => [id, manifest.name ?? id])
|
||||
)
|
||||
await writePnpFile(result.currentLockfile, {
|
||||
importerNames,
|
||||
lockfileDir: ctx.lockfileDir,
|
||||
virtualStoreDir: ctx.virtualStoreDir,
|
||||
registries: opts.registries,
|
||||
})
|
||||
}
|
||||
|
||||
ctx.pendingBuilds = ctx.pendingBuilds
|
||||
.filter((relDepPath) => !result.removedDepPaths.has(relDepPath))
|
||||
@@ -698,10 +716,15 @@ async function installInContext (
|
||||
const depPaths = Object.keys(dependenciesGraph)
|
||||
const rootNodes = depPaths.filter((depPath) => dependenciesGraph[depPath].depth === 0)
|
||||
|
||||
let extraEnv: Record<string, string> | undefined
|
||||
if (opts.enablePnp) {
|
||||
extraEnv = makeNodeRequireOption(path.join(opts.lockfileDir, '.pnp.js'))
|
||||
}
|
||||
await buildModules(dependenciesGraph, rootNodes, {
|
||||
childConcurrency: opts.childConcurrency,
|
||||
depsToBuild: new Set(result.newDepPaths),
|
||||
extraBinPaths: ctx.extraBinPaths,
|
||||
extraEnv,
|
||||
lockfileDir: ctx.lockfileDir,
|
||||
optional: opts.include.optionalDependencies,
|
||||
rawConfig: opts.rawConfig,
|
||||
|
||||
@@ -56,6 +56,24 @@ test('run pre/postinstall scripts', async (t: tape.Test) => {
|
||||
t.ok(lockfile.packages['/pre-and-postinstall-scripts-example/1.0.0'].requiresBuild, 'requiresBuild: true added to lockfile')
|
||||
})
|
||||
|
||||
test('run pre/postinstall scripts, when PnP is used and no symlinks', async (t: tape.Test) => {
|
||||
prepareEmpty(t)
|
||||
await addDependenciesToPackage({},
|
||||
['pre-and-postinstall-scripts-example'],
|
||||
await testDefaults({
|
||||
fastUnpack: false,
|
||||
enablePnp: true,
|
||||
symlink: false,
|
||||
targetDependenciesField: 'devDependencies',
|
||||
})
|
||||
)
|
||||
|
||||
const pkgDir = 'node_modules/.pnpm/pre-and-postinstall-scripts-example@1.0.0/node_modules/pre-and-postinstall-scripts-example'
|
||||
t.notOk(await exists(path.resolve(pkgDir, 'generated-by-prepare.js')))
|
||||
t.ok(await exists(path.resolve(pkgDir, 'generated-by-preinstall.js')))
|
||||
t.ok(await exists(path.resolve(pkgDir, 'generated-by-postinstall.js')))
|
||||
})
|
||||
|
||||
test('testing that the bins are linked when the package with the bins was already in node_modules', async (t: tape.Test) => {
|
||||
const project = prepareEmpty(t)
|
||||
|
||||
|
||||
@@ -1228,7 +1228,7 @@ test('memory consumption is under control on huge package with many peer depende
|
||||
t.ok(await exists('pnpm-lock.yaml'), 'lockfile created')
|
||||
})
|
||||
|
||||
test('installing with no symlinks', async (t) => {
|
||||
test('installing with no symlinks with PnP', async (t) => {
|
||||
const project = prepareEmpty(t)
|
||||
|
||||
await addDependenciesToPackage(
|
||||
@@ -1237,7 +1237,11 @@ test('installing with no symlinks', async (t) => {
|
||||
version: '0.0.0',
|
||||
},
|
||||
['rimraf@2.7.1'],
|
||||
await testDefaults({ fastUnpack: false, symlink: false })
|
||||
await testDefaults({
|
||||
enablePnp: true,
|
||||
fastUnpack: false,
|
||||
symlink: false,
|
||||
})
|
||||
)
|
||||
|
||||
t.deepEqual(await fs.readdir(path.resolve('node_modules')), ['.bin', '.modules.yaml', '.pnpm'])
|
||||
@@ -1245,6 +1249,7 @@ test('installing with no symlinks', async (t) => {
|
||||
|
||||
t.ok(await project.readCurrentLockfile())
|
||||
t.ok(await project.readModulesManifest())
|
||||
t.ok(await exists(path.resolve('.pnp.js')))
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
@@ -42,6 +42,9 @@
|
||||
{
|
||||
"path": "../lockfile-file"
|
||||
},
|
||||
{
|
||||
"path": "../lockfile-to-pnp"
|
||||
},
|
||||
{
|
||||
"path": "../lockfile-utils"
|
||||
},
|
||||
|
||||
6
pnpm-lock.yaml
generated
6
pnpm-lock.yaml
generated
@@ -672,6 +672,7 @@ importers:
|
||||
'@pnpm/lifecycle': 'link:../lifecycle'
|
||||
'@pnpm/link-bins': 'link:../link-bins'
|
||||
'@pnpm/lockfile-file': 'link:../lockfile-file'
|
||||
'@pnpm/lockfile-to-pnp': 'link:../lockfile-to-pnp'
|
||||
'@pnpm/lockfile-utils': 'link:../lockfile-utils'
|
||||
'@pnpm/modules-cleaner': 'link:../modules-cleaner'
|
||||
'@pnpm/modules-yaml': 'link:../modules-yaml'
|
||||
@@ -726,6 +727,7 @@ importers:
|
||||
'@pnpm/lifecycle': 'workspace:9.4.0'
|
||||
'@pnpm/link-bins': 'workspace:5.3.14'
|
||||
'@pnpm/lockfile-file': 'workspace:3.0.14'
|
||||
'@pnpm/lockfile-to-pnp': 'workspace:^0.2.0'
|
||||
'@pnpm/lockfile-utils': 'workspace:2.0.16'
|
||||
'@pnpm/logger': ^3.2.2
|
||||
'@pnpm/modules-cleaner': 'workspace:10.0.11'
|
||||
@@ -2000,6 +2002,7 @@ importers:
|
||||
'@pnpm/sort-packages': 'link:../sort-packages'
|
||||
'@pnpm/types': 'link:../types'
|
||||
p-limit: 3.0.2
|
||||
path-exists: 4.0.0
|
||||
ramda: 0.27.1
|
||||
realpath-missing: 1.0.0
|
||||
render-help: 1.0.0
|
||||
@@ -2033,6 +2036,7 @@ importers:
|
||||
execa: ^4.0.3
|
||||
mz: ^2.7.0
|
||||
p-limit: ^3.0.2
|
||||
path-exists: ^4.0.0
|
||||
ramda: ^0.27.1
|
||||
realpath-missing: ^1.0.0
|
||||
render-help: ^1.0.0
|
||||
@@ -2680,6 +2684,7 @@ importers:
|
||||
'@pnpm/lifecycle': 'link:../lifecycle'
|
||||
'@pnpm/link-bins': 'link:../link-bins'
|
||||
'@pnpm/lockfile-file': 'link:../lockfile-file'
|
||||
'@pnpm/lockfile-to-pnp': 'link:../lockfile-to-pnp'
|
||||
'@pnpm/lockfile-utils': 'link:../lockfile-utils'
|
||||
'@pnpm/lockfile-walker': 'link:../lockfile-walker'
|
||||
'@pnpm/manifest-utils': 'link:../manifest-utils'
|
||||
@@ -2776,6 +2781,7 @@ importers:
|
||||
'@pnpm/lifecycle': 'workspace:9.4.0'
|
||||
'@pnpm/link-bins': 'workspace:5.3.14'
|
||||
'@pnpm/lockfile-file': 'workspace:3.0.14'
|
||||
'@pnpm/lockfile-to-pnp': 'workspace:^0.2.0'
|
||||
'@pnpm/lockfile-utils': 'workspace:2.0.16'
|
||||
'@pnpm/lockfile-walker': 'workspace:3.0.4'
|
||||
'@pnpm/logger': ^3.2.2
|
||||
|
||||
Reference in New Issue
Block a user