Merge branch 'main' into v8

This commit is contained in:
Zoltan Kochan
2023-03-14 03:48:44 +02:00
169 changed files with 1299 additions and 232 deletions

View File

@@ -28,13 +28,16 @@ jobs:
gcc -I. -c -o lookup2.o lookup2.c
g++ -std=c++11 -o ldid lookup2.o ldid.cpp -I. -lcrypto -lplist -lxml2
sudo mv ldid /usr/local/bin
- name: install pnpm and npm
- name: install pnpm
run: |
curl -L https://get.pnpm.io/v6.16.js | node - add --global pnpm@next-8 npm@7
corepack enable
corepack prepare pnpm@next-8 --activate
- name: pnpm install
run: pnpm install
- name: Publish Packages
env:
# setting the "npm_config_//registry.npmjs.org/:_authToken" env variable directly doesn't work.
# probably "pnpm release" doesn't pass auth tokens to child processes
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
npm config set "//registry.npmjs.org/:_authToken" "${NPM_TOKEN}" # pnpm config set is broken

2
.gitignore vendored
View File

@@ -47,3 +47,5 @@ RELEASE.md
## custom modules-dir fixture
__fixtures__/custom-modules-dir/**/fake_modules/
__fixtures__/custom-modules-dir/cache/
__fixtures__/custom-modules-dir/patches/

View File

@@ -1,5 +1,12 @@
# @pnpm-private/updater
## 0.4.10
### Patch Changes
- Updated dependencies [787c43dcc]
- @pnpm/lockfile-file@7.0.6
## 0.4.9
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm-private/updater",
"version": "0.4.9",
"version": "0.4.10",
"private": true,
"type": "module",
"scripts": {

View File

@@ -84,17 +84,6 @@ To quote the [Rush](https://rushjs.io/) team:
</picture>
</a>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://doppler.com/?utm_source=pnpm&utm_medium=readme" target="_blank">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/doppler.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/doppler_light.svg" />
<img src="https://pnpm.io/img/users/doppler.svg" width="280" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<a href="https://depot.dev/?utm_source=pnpm&utm_medium=readme" target="_blank">
<picture>

View File

@@ -0,0 +1,11 @@
import { createServer } from 'http'
const server = createServer()
server.listen(9999, (err) => {
if (err) {
console.log(`[bar] dev error:`, err)
}
console.log(`[bar] server listen on 9999`)
setTimeout(() => {
throw new Error('[bar] server error, Oops')
}, 2000)
})

View File

@@ -0,0 +1,2 @@
import { spawn } from 'child_process'
spawn('node', ['./process-foo.js'], { stdio: 'inherit' })

View File

@@ -0,0 +1,10 @@
{
"name": "multiple-scripts-error-exit",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"dev:foo": "node ./dev-foo.js",
"dev:bar": "node ./dev-bar.js"
}
}

View File

@@ -0,0 +1,8 @@
import { createServer } from 'http'
const server = createServer()
server.listen(9990, (err) => {
if (err) {
console.log(`[foo] dev error:`, err)
}
console.log(`[foo] server listen on 9990`)
})

View File

@@ -1,5 +1,11 @@
# @pnpm/assert-project
## 2.3.21
### Patch Changes
- @pnpm/assert-store@1.0.58
## 2.3.20
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "@pnpm/assert-project",
"description": "Utils for testing projects that use pnpm",
"version": "2.3.20",
"version": "2.3.21",
"author": {
"name": "Zoltan Kochan",
"email": "z@kochan.io",

View File

@@ -1,5 +1,11 @@
# @pnpm/assert-store
## 1.0.58
### Patch Changes
- @pnpm/cafs@6.0.2
## 1.0.57
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "@pnpm/assert-store",
"description": "Utils for testing pnpm store",
"version": "1.0.57",
"version": "1.0.58",
"author": {
"name": "Zoltan Kochan",
"email": "z@kochan.io",

View File

@@ -126,17 +126,6 @@ function getChangelogEntry (changelog: string, version: string) {
</picture>
</a>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://doppler.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/doppler.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/doppler_light.svg" />
<img src="https://pnpm.io/img/users/doppler.svg" width="280" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<a href="https://depot.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank">
<picture>

View File

@@ -1,5 +1,11 @@
# @pnpm/prepare
## 0.0.64
### Patch Changes
- @pnpm/assert-project@2.3.21
## 0.0.63
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/prepare",
"version": "0.0.63",
"version": "0.0.64",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"dependencies": {

View File

@@ -1,5 +1,11 @@
# @pnpm/test-fixtures
## 0.0.33
### Patch Changes
- @pnpm/prepare@0.0.64
## 0.0.32
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "@pnpm/test-fixtures",
"description": "Test fixtures",
"version": "0.0.32",
"version": "0.0.33",
"author": {
"name": "Zoltan Kochan",
"email": "z@kochan.io",

View File

@@ -1,5 +1,14 @@
# @pnpm/cli-utils
## 1.1.5
### Patch Changes
- Updated dependencies [e505b58e3]
- @pnpm/config@17.0.0
- @pnpm/read-project-manifest@4.1.4
- @pnpm/default-reporter@11.0.40
## 1.1.4
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/cli-utils",
"version": "1.1.4",
"version": "1.1.5",
"description": "Utils for pnpm commands",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@@ -1,5 +1,12 @@
# @pnpm/default-reporter
## 11.0.40
### Patch Changes
- Updated dependencies [e505b58e3]
- @pnpm/config@17.0.0
## 11.0.39
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/default-reporter",
"version": "11.0.39",
"version": "11.0.40",
"description": "The default reporter of pnpm",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@@ -1,5 +1,16 @@
# @pnpm/config
## 17.0.0
### Major Changes
- e505b58e3: Don't extend NODE_PATH in command shims [#5176](https://github.com/pnpm/pnpm/issues/5176).
### Patch Changes
- @pnpm/read-project-manifest@4.1.4
- @pnpm/pnpmfile@4.0.38
## 16.7.2
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/config",
"version": "16.7.2",
"version": "17.0.0",
"description": "Gets configuration options for pnpm",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@@ -188,7 +188,7 @@ export async function getConfig (
'deploy-all-files': false,
'dedupe-peer-dependents': true,
'enable-modules-dir': true,
'extend-node-path': true,
'extend-node-path': false,
'fetch-retries': 2,
'fetch-retry-factor': 10,
'fetch-retry-maxtimeout': 60000,

View File

@@ -1,5 +1,19 @@
# @pnpm/plugin-commands-config
## 1.0.23
### Patch Changes
- Updated dependencies [e505b58e3]
- @pnpm/config@17.0.0
- @pnpm/cli-utils@1.1.5
## 1.0.22
### Patch Changes
- 6314a47b8: Settings related to authorization should be set/deleted by npm CLI [#6181](https://github.com/pnpm/pnpm/issues/6181).
## 1.0.21
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/plugin-commands-config",
"version": "1.0.21",
"version": "1.0.23",
"description": "Commands for reading and writing settings to/from config files",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@@ -33,6 +33,7 @@
"@pnpm/cli-utils": "workspace:*",
"@pnpm/config": "workspace:*",
"@pnpm/error": "workspace:*",
"@pnpm/run-npm": "workspace:*",
"ini": "3.0.1",
"read-ini-file": "4.0.0",
"render-help": "^1.0.3",

View File

@@ -5,6 +5,7 @@ export type ConfigCommandOptions = Pick<Config,
| 'cliOptions'
| 'dir'
| 'global'
| 'npmPath'
| 'rawConfig'
> & {
json?: boolean

View File

@@ -1,10 +1,20 @@
import path from 'path'
import { runNpm } from '@pnpm/run-npm'
import { readIniFile } from 'read-ini-file'
import { writeIniFile } from 'write-ini-file'
import { ConfigCommandOptions } from './ConfigCommandOptions'
export async function configSet (opts: ConfigCommandOptions, key: string, value: string | null) {
const configPath = opts.global ? path.join(opts.configDir, 'rc') : path.join(opts.dir, '.npmrc')
if (opts.global && settingShouldFallBackToNpm(key)) {
const _runNpm = runNpm.bind(null, opts.npmPath)
if (value == null) {
_runNpm(['config', 'delete', key])
} else {
_runNpm(['config', 'set', `${key}=${value}`])
}
return
}
const settings = await safeReadIniFile(configPath)
if (value == null) {
if (settings[key] == null) return
@@ -15,6 +25,14 @@ export async function configSet (opts: ConfigCommandOptions, key: string, value:
await writeIniFile(configPath, settings)
}
function settingShouldFallBackToNpm (key: string): boolean {
return (
['registry', '_auth', '_authToken', 'username', '_password'].includes(key) ||
key[0] === '@' ||
key.startsWith('//')
)
}
async function safeReadIniFile (configPath: string): Promise<Record<string, unknown>> {
try {
return await readIniFile(configPath) as Record<string, unknown>

View File

@@ -0,0 +1,33 @@
import { config } from '@pnpm/plugin-commands-config'
import { runNpm } from '@pnpm/run-npm'
jest.mock('@pnpm/run-npm', () => ({
runNpm: jest.fn(),
}))
describe.each(
[
'_auth',
'_authToken',
'_password',
'username',
'registry',
'@foo:registry',
'//registry.npmjs.org/:_authToken',
]
)('settings related to auth are handled by npm CLI', (key) => {
const configOpts = {
dir: process.cwd(),
cliOptions: {},
configDir: __dirname, // this doesn't matter, it won't be used
rawConfig: {},
}
it(`should set ${key}`, async () => {
await config.handler(configOpts, ['set', `${key}=123`])
expect(runNpm).toHaveBeenCalledWith(undefined, ['config', 'set', `${key}=123`])
})
it(`should delete ${key}`, async () => {
await config.handler(configOpts, ['delete', key])
expect(runNpm).toHaveBeenCalledWith(undefined, ['config', 'delete', key])
})
})

View File

@@ -15,6 +15,9 @@
{
"path": "../../cli/cli-utils"
},
{
"path": "../../exec/run-npm"
},
{
"path": "../../packages/error"
},

View File

@@ -1,5 +1,12 @@
# @pnpm/node.fetcher
## 2.0.14
### Patch Changes
- @pnpm/tarball-fetcher@14.1.4
- @pnpm/create-cafs-store@3.1.6
## 2.0.13
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/node.fetcher",
"version": "2.0.13",
"version": "2.0.14",
"description": "Node.js artifacts fetcher",
"funding": "https://opencollective.com/pnpm",
"main": "lib/index.js",

View File

@@ -1,5 +1,11 @@
# @pnpm/node.resolver
## 1.1.11
### Patch Changes
- @pnpm/node.fetcher@2.0.14
## 1.1.10
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/node.resolver",
"version": "1.1.10",
"version": "1.1.11",
"description": "Resolves a Node.js version specifier to an exact Node.js version",
"funding": "https://opencollective.com/pnpm",
"main": "lib/index.js",

View File

@@ -1,5 +1,15 @@
# @pnpm/plugin-commands-env
## 3.1.34
### Patch Changes
- Updated dependencies [e505b58e3]
- @pnpm/config@17.0.0
- @pnpm/cli-utils@1.1.5
- @pnpm/node.fetcher@2.0.14
- @pnpm/node.resolver@1.1.11
## 3.1.33
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/plugin-commands-env",
"version": "3.1.33",
"version": "3.1.34",
"description": "pnpm commands for managing Node.js",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@@ -1,5 +1,13 @@
# @pnpm/build-modules
## 10.1.7
### Patch Changes
- @pnpm/link-bins@8.0.9
- @pnpm/lifecycle@14.1.7
- @pnpm/fs.hard-link-dir@1.0.3
## 10.1.6
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/build-modules",
"version": "10.1.6",
"version": "10.1.7",
"description": "Build packages in node_modules",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@@ -1,5 +1,11 @@
# @pnpm/lifecycle
## 14.1.7
### Patch Changes
- @pnpm/directory-fetcher@5.1.6
## 14.1.6
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/lifecycle",
"version": "14.1.6",
"version": "14.1.7",
"description": "Package lifecycle hook runner",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@@ -1,5 +1,19 @@
# @pnpm/plugin-commands-rebuild
## 7.1.5
### Patch Changes
- Updated dependencies [e505b58e3]
- @pnpm/config@17.0.0
- @pnpm/get-context@8.2.4
- @pnpm/cli-utils@1.1.5
- @pnpm/store-connection-manager@5.2.18
- @pnpm/link-bins@8.0.9
- @pnpm/find-workspace-packages@5.0.40
- @pnpm/lifecycle@14.1.7
- @pnpm/fs.hard-link-dir@1.0.3
## 7.1.4
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/plugin-commands-rebuild",
"version": "7.1.4",
"version": "7.1.5",
"description": "Commands for rebuilding dependencies",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@@ -1,5 +1,16 @@
# @pnpm/plugin-commands-script-runners
## 6.5.5
### Patch Changes
- Updated dependencies [e505b58e3]
- @pnpm/config@17.0.0
- @pnpm/plugin-commands-installation@11.5.5
- @pnpm/read-project-manifest@4.1.4
- @pnpm/cli-utils@1.1.5
- @pnpm/lifecycle@14.1.7
## 6.5.4
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/plugin-commands-script-runners",
"version": "6.5.4",
"version": "6.5.5",
"description": "Commands for running scripts",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@@ -1,5 +1,11 @@
# @pnpm/prepare-package
## 4.1.2
### Patch Changes
- @pnpm/lifecycle@14.1.7
## 4.1.1
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/prepare-package",
"version": "4.1.1",
"version": "4.1.2",
"description": "Prepares a Git-hosted package",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@@ -1,5 +1,11 @@
# @pnpm/directory-fetcher
## 5.1.6
### Patch Changes
- @pnpm/read-project-manifest@4.1.4
## 5.1.5
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/directory-fetcher",
"version": "5.1.5",
"version": "5.1.6",
"description": "A fetcher for local directory packages",
"funding": "https://opencollective.com/pnpm",
"main": "lib/index.js",

View File

@@ -1,5 +1,11 @@
# @pnpm/git-fetcher
## 8.0.2
### Patch Changes
- @pnpm/prepare-package@4.1.2
## 8.0.1
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/git-fetcher",
"version": "8.0.1",
"version": "8.0.2",
"description": "A fetcher for git-hosted packages",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@@ -1,5 +1,13 @@
# @pnpm/tarball-fetcher
## 14.1.4
### Patch Changes
- Updated dependencies [955874422]
- @pnpm/graceful-fs@2.1.0
- @pnpm/prepare-package@4.1.2
## 14.1.3
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/tarball-fetcher",
"version": "14.1.3",
"version": "14.1.4",
"description": "Fetcher for packages hosted as tarballs",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@@ -1,5 +1,11 @@
# @pnpm/fs.find-packages
## 1.0.3
### Patch Changes
- @pnpm/read-project-manifest@4.1.4
## 1.0.2
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/fs.find-packages",
"version": "1.0.2",
"version": "1.0.3",
"description": "Find all packages inside a directory",
"main": "lib/index.js",
"files": [

View File

@@ -1,5 +1,11 @@
# @pnpm/graceful-fs
## 2.1.0
### Minor Changes
- 955874422: Add copyFile, link, stat.
## 2.0.0
### Major Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/graceful-fs",
"version": "2.0.0",
"version": "2.1.0",
"description": "Promisified graceful-fs",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@@ -2,7 +2,10 @@ import { promisify } from 'util'
import gfs from 'graceful-fs'
export default { // eslint-disable-line
copyFile: promisify(gfs.copyFile),
createReadStream: gfs.createReadStream,
link: promisify(gfs.link),
readFile: promisify(gfs.readFile),
stat: promisify(gfs.stat),
writeFile: promisify(gfs.writeFile),
}

View File

@@ -1,5 +1,13 @@
# @pnpm/fs.indexed-pkg-importer
## 2.1.4
### Patch Changes
- 955874422: Retry copying file on EBUSY error [#6201](https://github.com/pnpm/pnpm/issues/6201).
- Updated dependencies [955874422]
- @pnpm/graceful-fs@2.1.0
## 2.1.3
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "@pnpm/fs.indexed-pkg-importer",
"description": "Replicates indexed directories using hard links, copies, or cloning",
"version": "2.1.3",
"version": "2.1.4",
"bugs": {
"url": "https://github.com/pnpm/pnpm/issues"
},
@@ -16,6 +16,7 @@
},
"dependencies": {
"@pnpm/core-loggers": "workspace:*",
"@pnpm/graceful-fs": "workspace:*",
"@pnpm/store-controller-types": "workspace:*",
"@zkochan/rimraf": "^2.1.2",
"fs-extra": "^11.1.0",

View File

@@ -1,4 +1,5 @@
import { constants, promises as fs, Stats } from 'fs'
import { constants, Stats } from 'fs'
import fs from '@pnpm/graceful-fs'
import path from 'path'
import { globalInfo, globalWarn } from '@pnpm/logger'
import { packageImportMethodLogger } from '@pnpm/core-loggers'

View File

@@ -1,31 +1,43 @@
import fs from 'fs'
import path from 'path'
import { createIndexedPkgImporter } from '@pnpm/fs.indexed-pkg-importer'
import gfs from '@pnpm/graceful-fs'
import { globalInfo } from '@pnpm/logger'
const fsMock = { promises: {} as any } as any // eslint-disable-line
jest.mock('fs', () => {
const { access, constants, promises } = jest.requireActual('fs')
fsMock.constants = constants
fsMock.promises.mkdir = promises.mkdir
fsMock.promises.readdir = promises.readdir
fsMock.access = access
return fsMock
jest.mock('@pnpm/graceful-fs', () => {
const { access, promises } = jest.requireActual('fs')
const fsMock = {
mkdir: promises.mkdir,
readdir: promises.readdir,
access,
copyFile: jest.fn(),
link: jest.fn(),
stat: jest.fn(),
}
return {
__esModule: true,
default: fsMock,
}
})
jest.mock('path-temp', () => (dir: string) => path.join(dir, '_tmp'))
jest.mock('rename-overwrite', () => jest.fn())
jest.mock('fs-extra', () => ({
copy: jest.fn(),
}))
const globalInfo = jest.fn()
const globalWarn = jest.fn()
const logger = jest.fn(() => ({ debug: jest.fn() }))
jest.mock('@pnpm/logger', () => ({ logger, globalWarn, globalInfo }))
jest.mock('@pnpm/logger', () => ({
logger: jest.fn(() => ({ debug: jest.fn() })),
globalWarn: jest.fn(),
globalInfo: jest.fn(),
}))
// eslint-disable-next-line
import { createIndexedPkgImporter } from '@pnpm/fs.indexed-pkg-importer'
beforeEach(() => {
;(gfs.copyFile as jest.Mock).mockClear()
;(gfs.link as jest.Mock).mockClear()
;(globalInfo as jest.Mock).mockReset()
})
test('packageImportMethod=auto: clone files by default', async () => {
const importPackage = createIndexedPkgImporter('auto')
fsMock.promises.copyFile = jest.fn()
fsMock.promises.rename = jest.fn()
expect(await importPackage('project/package', {
filesMap: {
'index.js': 'hash2',
@@ -34,25 +46,23 @@ test('packageImportMethod=auto: clone files by default', async () => {
force: false,
fromStore: false,
})).toBe('clone')
expect(fsMock.promises.copyFile).toBeCalledWith(
expect(gfs.copyFile).toBeCalledWith(
path.join('hash1'),
path.join('project', '_tmp', 'package.json'),
fsMock.constants.COPYFILE_FICLONE_FORCE
fs.constants.COPYFILE_FICLONE_FORCE
)
expect(fsMock.promises.copyFile).toBeCalledWith(
expect(gfs.copyFile).toBeCalledWith(
path.join('hash2'),
path.join('project', '_tmp', 'index.js'),
fsMock.constants.COPYFILE_FICLONE_FORCE
fs.constants.COPYFILE_FICLONE_FORCE
)
})
test('packageImportMethod=auto: link files if cloning fails', async () => {
const importPackage = createIndexedPkgImporter('auto')
fsMock.promises.copyFile = jest.fn(() => {
;(gfs.copyFile as jest.Mock).mockImplementation(async () => {
throw new Error('This file system does not support cloning')
})
fsMock.promises.link = jest.fn()
fsMock.promises.rename = jest.fn()
expect(await importPackage('project/package', {
filesMap: {
'index.js': 'hash2',
@@ -61,10 +71,10 @@ test('packageImportMethod=auto: link files if cloning fails', async () => {
force: false,
fromStore: false,
})).toBe('hardlink')
expect(fsMock.promises.link).toBeCalledWith(path.join('hash1'), path.join('project', '_tmp', 'package.json'))
expect(fsMock.promises.link).toBeCalledWith(path.join('hash2'), path.join('project', '_tmp', 'index.js'))
expect(fsMock.promises.copyFile).toBeCalled()
fsMock.promises.copyFile.mockClear()
expect(gfs.link).toBeCalledWith(path.join('hash1'), path.join('project', '_tmp', 'package.json'))
expect(gfs.link).toBeCalledWith(path.join('hash2'), path.join('project', '_tmp', 'index.js'))
expect(gfs.copyFile).toBeCalled()
;(gfs.copyFile as jest.Mock).mockClear()
// The copy function will not be called again
expect(await importPackage('project2/package', {
@@ -75,24 +85,23 @@ test('packageImportMethod=auto: link files if cloning fails', async () => {
force: false,
fromStore: false,
})).toBe('hardlink')
expect(fsMock.promises.copyFile).not.toBeCalled()
expect(fsMock.promises.link).toBeCalledWith(path.join('hash1'), path.join('project2', '_tmp', 'package.json'))
expect(fsMock.promises.link).toBeCalledWith(path.join('hash2'), path.join('project2', '_tmp', 'index.js'))
expect(gfs.copyFile).not.toBeCalled()
expect(gfs.link).toBeCalledWith(path.join('hash1'), path.join('project2', '_tmp', 'package.json'))
expect(gfs.link).toBeCalledWith(path.join('hash2'), path.join('project2', '_tmp', 'index.js'))
})
test('packageImportMethod=auto: link files if cloning fails and even hard linking fails but not with EXDEV error', async () => {
const importPackage = createIndexedPkgImporter('auto')
fsMock.promises.copyFile = jest.fn(() => {
;(gfs.copyFile as jest.Mock).mockImplementation(async () => {
throw new Error('This file system does not support cloning')
})
let linkFirstCall = true
fsMock.promises.link = jest.fn(() => {
;(gfs.link as jest.Mock).mockImplementation(async () => {
if (linkFirstCall) {
linkFirstCall = false
throw new Error()
}
})
fsMock.promises.rename = jest.fn()
expect(await importPackage('project/package', {
filesMap: {
'index.js': 'hash2',
@@ -100,22 +109,21 @@ test('packageImportMethod=auto: link files if cloning fails and even hard linkin
force: false,
fromStore: false,
})).toBe('hardlink')
expect(fsMock.promises.link).toBeCalledWith(path.join('hash2'), path.join('project', '_tmp', 'index.js'))
expect(fsMock.promises.link).toBeCalledTimes(2)
expect(fsMock.promises.copyFile).toBeCalledTimes(1)
expect(gfs.link).toBeCalledWith(path.join('hash2'), path.join('project', '_tmp', 'index.js'))
expect(gfs.link).toBeCalledTimes(2)
expect(gfs.copyFile).toBeCalledTimes(1)
})
test('packageImportMethod=auto: chooses copying if cloning and hard linking is not possible', async () => {
const importPackage = createIndexedPkgImporter('auto')
fsMock.promises.copyFile = jest.fn((src: string, dest: string, flags?: number) => {
if (flags === fsMock.constants.COPYFILE_FICLONE_FORCE) {
;(gfs.copyFile as jest.Mock).mockImplementation(async (src: string, dest: string, flags?: number) => {
if (flags === fs.constants.COPYFILE_FICLONE_FORCE) {
throw new Error('This file system does not support cloning')
}
})
fsMock.promises.link = jest.fn(() => {
;(gfs.link as jest.Mock).mockImplementation(() => {
throw new Error('EXDEV: cross-device link not permitted')
})
fsMock.promises.rename = jest.fn()
expect(await importPackage('project/package', {
filesMap: {
'index.js': 'hash2',
@@ -123,19 +131,18 @@ test('packageImportMethod=auto: chooses copying if cloning and hard linking is n
force: false,
fromStore: false,
})).toBe('copy')
expect(fsMock.promises.copyFile).toBeCalledWith(path.join('hash2'), path.join('project', '_tmp', 'index.js'))
expect(fsMock.promises.copyFile).toBeCalledTimes(2)
expect(gfs.copyFile).toBeCalledWith(path.join('hash2'), path.join('project', '_tmp', 'index.js'))
expect(gfs.copyFile).toBeCalledTimes(2)
})
test('packageImportMethod=hardlink: fall back to copying if hardlinking fails', async () => {
const importPackage = createIndexedPkgImporter('hardlink')
fsMock.promises.link = jest.fn((src: string, dest: string) => {
;(gfs.link as jest.Mock).mockImplementation(async (src: string, dest: string) => {
if (dest.endsWith('license')) {
throw Object.assign(new Error(''), { code: 'EEXIST' })
}
throw new Error('This file system does not support hard linking')
})
fsMock.promises.copyFile = jest.fn()
expect(await importPackage('project/package', {
filesMap: {
'index.js': 'hash2',
@@ -145,17 +152,15 @@ test('packageImportMethod=hardlink: fall back to copying if hardlinking fails',
force: false,
fromStore: false,
})).toBe('hardlink')
expect(fsMock.promises.link).toBeCalledTimes(3)
expect(fsMock.promises.copyFile).toBeCalledTimes(2) // One time the target already exists, so it won't be copied
expect(fsMock.promises.copyFile).toBeCalledWith(path.join('hash1'), path.join('project', '_tmp', 'package.json'))
expect(fsMock.promises.copyFile).toBeCalledWith(path.join('hash2'), path.join('project', '_tmp', 'index.js'))
expect(gfs.link).toBeCalledTimes(3)
expect(gfs.copyFile).toBeCalledTimes(2) // One time the target already exists, so it won't be copied
expect(gfs.copyFile).toBeCalledWith(path.join('hash1'), path.join('project', '_tmp', 'package.json'))
expect(gfs.copyFile).toBeCalledWith(path.join('hash2'), path.join('project', '_tmp', 'index.js'))
})
test('packageImportMethod=hardlink does not relink package from store if package.json is linked from the store', async () => {
const importPackage = createIndexedPkgImporter('hardlink')
fsMock.promises.copyFile = jest.fn()
fsMock.promises.rename = jest.fn()
fsMock.promises.stat = jest.fn(() => ({ ino: 1 }))
;(gfs.stat as jest.Mock).mockReturnValue(Promise.resolve({ ino: 1 }))
expect(await importPackage('project/package', {
filesMap: {
'index.js': 'hash2',
@@ -167,12 +172,9 @@ test('packageImportMethod=hardlink does not relink package from store if package
})
test('packageImportMethod=hardlink relinks package from store if package.json is not linked from the store', async () => {
globalInfo.mockReset()
const importPackage = createIndexedPkgImporter('hardlink')
fsMock.promises.copyFile = jest.fn()
fsMock.promises.rename = jest.fn()
let ino = 0
fsMock.promises.stat = jest.fn(() => ({ ino: ++ino }))
;(gfs.stat as jest.Mock).mockImplementation(async () => ({ ino: ++ino }))
expect(await importPackage('project/package', {
filesMap: {
'index.js': 'hash2',
@@ -186,9 +188,7 @@ test('packageImportMethod=hardlink relinks package from store if package.json is
test('packageImportMethod=hardlink does not relink package from store if package.json is not present in the store', async () => {
const importPackage = createIndexedPkgImporter('hardlink')
fsMock.promises.copyFile = jest.fn()
fsMock.promises.rename = jest.fn()
fsMock.promises.stat = jest.fn((file) => {
;(gfs.stat as jest.Mock).mockImplementation(async (file) => {
expect(typeof file).toBe('string')
return { ino: 1 }
})
@@ -202,11 +202,8 @@ test('packageImportMethod=hardlink does not relink package from store if package
})
test('packageImportMethod=hardlink links packages when they are not found', async () => {
globalInfo.mockReset()
const importPackage = createIndexedPkgImporter('hardlink')
fsMock.promises.copyFile = jest.fn()
fsMock.promises.rename = jest.fn()
fsMock.promises.stat = jest.fn((file) => {
;(gfs.stat as jest.Mock).mockImplementation(async (file) => {
if (file === path.join('project/package', 'package.json')) {
throw Object.assign(new Error(), { code: 'ENOENT' })
}

View File

@@ -17,6 +17,9 @@
},
{
"path": "../../store/store-controller-types"
},
{
"path": "../graceful-fs"
}
],
"composite": true

View File

@@ -1,5 +1,11 @@
# @pnpm/pnpmfile
## 4.0.38
### Patch Changes
- @pnpm/core@8.0.2
## 4.0.37
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/pnpmfile",
"version": "4.0.37",
"version": "4.0.38",
"description": "Reading a .pnpmfile.cjs",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@@ -1,5 +1,14 @@
# @pnpm/audit
## 6.1.8
### Patch Changes
- 185ab01ad: When patch package does not specify a version, use locally installed version by default [#6192](https://github.com/pnpm/pnpm/issues/6192).
- Updated dependencies [185ab01ad]
- @pnpm/list@8.2.2
- @pnpm/read-project-manifest@4.1.4
## 6.1.7
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/audit",
"version": "6.1.7",
"version": "6.1.8",
"description": "Audit a lockfile",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@@ -6,7 +6,7 @@ import { Lockfile } from '@pnpm/lockfile-types'
import { DependenciesField } from '@pnpm/types'
import { lockfileToAuditTree } from './lockfileToAuditTree'
import { AuditReport } from './types'
import { searchForPackages, PackageNode } from '@pnpm/list'
import { searchForPackages, flattenSearchedPackages } from '@pnpm/list'
export * from './types'
@@ -95,28 +95,7 @@ async function searchPackagePaths (
pkg: string
) {
const pkgs = await searchForPackages([pkg], projectDirs, searchOpts)
const paths: string[] = []
for (const pkg of pkgs) {
_walker([
...(pkg.optionalDependencies ?? []),
...(pkg.dependencies ?? []),
...(pkg.devDependencies ?? []),
...(pkg.unsavedDependencies ?? []),
], path.relative(searchOpts.lockfileDir, pkg.path) || '.')
}
return paths
function _walker (packages: PackageNode[], depPath: string) {
for (const pkg of packages) {
const nextDepPath = `${depPath} > ${pkg.name}@${pkg.version}`
if (pkg.dependencies?.length) {
_walker(pkg.dependencies, nextDepPath)
} else {
paths.push(nextDepPath)
}
}
}
return flattenSearchedPackages(pkgs, { lockfileDir: searchOpts.lockfileDir }).map(({ depPath }) => depPath)
}
export class AuditEndpointNotExistsError extends PnpmError {

View File

@@ -1,5 +1,11 @@
# @pnpm/lockfile-file
## 7.0.6
### Patch Changes
- 787c43dcc: `patchedDependencies` are now sorted consistently in the lockfile [#6208](https://github.com/pnpm/pnpm/pull/6208).
## 7.0.5
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/lockfile-file",
"version": "7.0.5",
"version": "7.0.6",
"description": "Read/write pnpm-lock.yaml files",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@@ -77,9 +77,9 @@ export function sortLockfileKeys (lockfile: LockfileFile) {
})
}
}
for (const key of ['specifiers', 'dependencies', 'devDependencies', 'optionalDependencies', 'time'] as const) {
for (const key of ['specifiers', 'dependencies', 'devDependencies', 'optionalDependencies', 'time', 'patchedDependencies'] as const) {
if (!lockfile[key]) continue
lockfile[key] = sortKeys(lockfile[key]!)
lockfile[key] = sortKeys<any>(lockfile[key]) // eslint-disable-line @typescript-eslint/no-explicit-any
}
return sortKeys(lockfile, { compare: compareRootKeys })
}

View File

@@ -26,6 +26,11 @@ test('sorts keys alphabetically', () => {
},
},
},
patchedDependencies: {
zzz: { path: 'foo', hash: 'bar' },
bar: { path: 'foo', hash: 'bar' },
aaa: { path: 'foo', hash: 'bar' },
},
})
expect(normalizedLockfile).toStrictEqual({
@@ -52,9 +57,15 @@ test('sorts keys alphabetically', () => {
},
},
},
patchedDependencies: {
aaa: { path: 'foo', hash: 'bar' },
bar: { path: 'foo', hash: 'bar' },
zzz: { path: 'foo', hash: 'bar' },
},
})
expect(Object.keys(normalizedLockfile.importers?.foo.dependencies ?? {})).toStrictEqual(['aaa', 'bar', 'zzz'])
expect(Object.keys(normalizedLockfile.importers?.foo.specifiers ?? {})).toStrictEqual(['aaa', 'bar', 'zzz'])
expect(Object.keys(normalizedLockfile.patchedDependencies ?? {})).toStrictEqual(['aaa', 'bar', 'zzz'])
})
test('sorting does not care about locale (e.g. Czech has "ch" as a single character after "h")', () => {

View File

@@ -1,5 +1,12 @@
# @pnpm/lockfile-to-pnp
## 2.0.14
### Patch Changes
- Updated dependencies [787c43dcc]
- @pnpm/lockfile-file@7.0.6
## 2.0.13
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/lockfile-to-pnp",
"version": "2.0.13",
"version": "2.0.14",
"description": "Creates a Plug'n'Play file from a pnpm-lock.yaml",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@@ -1,5 +1,18 @@
# @pnpm/plugin-commands-audit
## 7.2.13
### Patch Changes
- Updated dependencies [185ab01ad]
- Updated dependencies [787c43dcc]
- Updated dependencies [e505b58e3]
- @pnpm/audit@6.1.8
- @pnpm/lockfile-file@7.0.6
- @pnpm/config@17.0.0
- @pnpm/read-project-manifest@4.1.4
- @pnpm/cli-utils@1.1.5
## 7.2.12
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/plugin-commands-audit",
"version": "7.2.12",
"version": "7.2.13",
"description": "pnpm commands for dependencies audit",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@@ -1,5 +1,14 @@
# @pnpm/make-dedicated-lockfile
## 0.4.14
### Patch Changes
- Updated dependencies [787c43dcc]
- @pnpm/lockfile-file@7.0.6
- @pnpm/read-project-manifest@4.1.4
- @pnpm/exportable-manifest@4.0.8
## 0.4.13
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/make-dedicated-lockfile",
"version": "0.4.13",
"version": "0.4.14",
"description": "Creates a dedicated lockfile for a subset of workspace projects",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@@ -1,5 +1,15 @@
# @pnpm/mount-modules
## 0.3.40
### Patch Changes
- Updated dependencies [787c43dcc]
- Updated dependencies [e505b58e3]
- @pnpm/lockfile-file@7.0.6
- @pnpm/config@17.0.0
- @pnpm/cafs@6.0.2
## 0.3.39
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/mount-modules",
"version": "0.3.39",
"version": "0.3.40",
"description": "Mounts a node_modules directory with FUSE",
"main": "lib/index.js",
"bin": "bin/mount-modules.js",

View File

@@ -1,5 +1,13 @@
# @pnpm/plugin-commands-doctor
## 1.0.38
### Patch Changes
- Updated dependencies [e505b58e3]
- @pnpm/config@17.0.0
- @pnpm/cli-utils@1.1.5
## 1.0.37
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/plugin-commands-doctor",
"version": "1.0.37",
"version": "1.0.38",
"description": "Commands for checks of known common issues ",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@@ -1,5 +1,13 @@
# @pnpm/plugin-commands-init
## 2.0.40
### Patch Changes
- Updated dependencies [e505b58e3]
- @pnpm/config@17.0.0
- @pnpm/cli-utils@1.1.5
## 2.0.39
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/plugin-commands-init",
"version": "2.0.39",
"version": "2.0.40",
"description": "Create a package.json file",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@@ -1,5 +1,11 @@
# @pnpm/plugin-commands-setup
## 3.0.40
### Patch Changes
- @pnpm/cli-utils@1.1.5
## 3.0.39
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/plugin-commands-setup",
"version": "3.0.39",
"version": "3.0.40",
"description": "pnpm commands for setting up pnpm",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@@ -1,5 +1,19 @@
# @pnpm/plugin-commands-patching
## 2.1.14
### Patch Changes
- 185ab01ad: When patch package does not specify a version, use locally installed version by default [#6192](https://github.com/pnpm/pnpm/issues/6192).
- Updated dependencies [787c43dcc]
- Updated dependencies [e505b58e3]
- @pnpm/lockfile-file@7.0.6
- @pnpm/config@17.0.0
- @pnpm/plugin-commands-installation@11.5.5
- @pnpm/read-project-manifest@4.1.4
- @pnpm/cli-utils@1.1.5
- @pnpm/store-connection-manager@5.2.18
## 2.1.13
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/plugin-commands-patching",
"version": "2.1.13",
"version": "2.1.14",
"description": "Commands for creating patches",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@@ -37,13 +37,17 @@
"@pnpm/plugin-commands-patching": "workspace:*",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "3.5.0",
"@pnpm/test-fixtures": "workspace:*",
"@types/ramda": "0.28.20",
"@types/semver": "7.3.13",
"write-yaml-file": "^4.2.0"
},
"dependencies": {
"@pnpm/cli-utils": "workspace:*",
"@pnpm/config": "workspace:*",
"@pnpm/constants": "workspace:*",
"@pnpm/error": "workspace:*",
"@pnpm/modules-yaml": "workspace:*",
"@pnpm/parse-wanted-dependency": "workspace:*",
"@pnpm/patching.apply-patch": "workspace:*",
"@pnpm/pick-registry-for-package": "workspace:*",
@@ -51,10 +55,15 @@
"@pnpm/read-package-json": "workspace:*",
"@pnpm/read-project-manifest": "workspace:*",
"@pnpm/store-connection-manager": "workspace:*",
"@pnpm/lockfile-file": "workspace:*",
"@pnpm/lockfile-utils": "workspace:*",
"enquirer": "^2.3.6",
"escape-string-regexp": "^4.0.0",
"ramda": "npm:@pnpm/ramda@0.28.1",
"realpath-missing": "^1.1.0",
"render-help": "^1.0.3",
"safe-execa": "^0.1.3",
"semver": "^7.3.8",
"tempy": "^1.0.1"
},
"peerDependencies": {

View File

@@ -0,0 +1,72 @@
import path from 'path'
import { parseWantedDependency, ParseWantedDependencyResult } from '@pnpm/parse-wanted-dependency'
import { prompt } from 'enquirer'
import { readCurrentLockfile } from '@pnpm/lockfile-file'
import { nameVerFromPkgSnapshot } from '@pnpm/lockfile-utils'
import { PnpmError } from '@pnpm/error'
import { WANTED_LOCKFILE } from '@pnpm/constants'
import { readModulesManifest } from '@pnpm/modules-yaml'
import realpathMissing from 'realpath-missing'
import semver from 'semver'
import { Config } from '@pnpm/config'
type GetPatchedDependencyOptions = {
lockfileDir: string
} & Pick<Config, 'virtualStoreDir' | 'modulesDir'>
export async function getPatchedDependency (rawDependency: string, opts: GetPatchedDependencyOptions): Promise<ParseWantedDependencyResult> {
const dep = parseWantedDependency(rawDependency)
const { versions, preferredVersions } = await getVersionsFromLockfile(dep, opts)
if (!preferredVersions.length) {
throw new PnpmError(
'PATCH_VERSION_NOT_FOUND',
`Can not find ${rawDependency} in project ${opts.lockfileDir}, ${versions.length ? `you can specify currently installed version: ${versions.join(', ')}.` : `did you forget to install ${rawDependency}?`}`
)
}
dep.alias = dep.alias ?? rawDependency
if (preferredVersions.length > 1) {
const { version } = await prompt<{
version: string
}>({
type: 'select',
name: 'version',
message: 'Choose which version to patch',
choices: versions,
})
dep.pref = version
} else {
dep.pref = preferredVersions[0]
}
return dep
}
async function getVersionsFromLockfile (dep: ParseWantedDependencyResult, opts: GetPatchedDependencyOptions) {
const modulesDir = await realpathMissing(path.join(opts.lockfileDir, opts.modulesDir ?? 'node_modules'))
const modules = await readModulesManifest(modulesDir)
const lockfile = (modules?.virtualStoreDir && await readCurrentLockfile(modules.virtualStoreDir, {
ignoreIncompatible: true,
})) ?? null
if (!lockfile) {
throw new PnpmError(
'PATCH_NO_LOCKFILE',
`No ${WANTED_LOCKFILE} found: Cannot patch without a lockfile`
)
}
const pkgName = dep.alias && dep.pref ? dep.alias : (dep.pref ?? dep.alias)
const versions = Object.entries(lockfile.packages ?? {})
.map(([depPath, pkgSnapshot]) => nameVerFromPkgSnapshot(depPath, pkgSnapshot))
.filter(({ name }) => name === pkgName)
.map(({ version }) => version)
return {
versions,
preferredVersions: versions.filter(version => dep.alias && dep.pref ? semver.satisfies(version, dep.pref) : true),
}
}

View File

@@ -11,7 +11,9 @@ import pick from 'ramda/src/pick'
import renderHelp from 'render-help'
import tempy from 'tempy'
import { PnpmError } from '@pnpm/error'
import { writePackage, ParseWantedDependencyResult } from './writePackage'
import { ParseWantedDependencyResult } from '@pnpm/parse-wanted-dependency'
import { writePackage } from './writePackage'
import { getPatchedDependency } from './getPatchedDependency'
export function rcOptionsTypes () {
return pick([], allTypes)
@@ -48,7 +50,16 @@ export function help () {
})
}
export type PatchCommandOptions = Pick<Config, 'dir' | 'registries' | 'tag' | 'storeDir' | 'rootProjectManifest' | 'lockfileDir'> & CreateStoreControllerOptions & {
export type PatchCommandOptions = Pick<Config,
| 'dir'
| 'registries'
| 'tag'
| 'storeDir'
| 'rootProjectManifest'
| 'lockfileDir'
| 'modulesDir'
| 'virtualStoreDir'
> & CreateStoreControllerOptions & {
editDir?: string
reporter?: (logObj: LogBase) => void
ignoreExisting?: boolean
@@ -58,14 +69,24 @@ export async function handler (opts: PatchCommandOptions, params: string[]) {
if (opts.editDir && fs.existsSync(opts.editDir) && fs.readdirSync(opts.editDir).length > 0) {
throw new PnpmError('PATCH_EDIT_DIR_EXISTS', `The target directory already exists: '${opts.editDir}'`)
}
if (!params[0]) {
throw new PnpmError('MISSING_PACKAGE_NAME', '`pnpm patch` requires the package name')
}
const editDir = opts.editDir ?? tempy.directory()
const patchedDep = await writePackage(params[0], editDir, opts)
const lockfileDir = opts.lockfileDir ?? opts.dir ?? process.cwd()
const patchedDep = await getPatchedDependency(params[0], {
lockfileDir,
modulesDir: opts.modulesDir,
virtualStoreDir: opts.virtualStoreDir,
})
await writePackage(patchedDep, editDir, opts)
if (!opts.ignoreExisting && opts.rootProjectManifest?.pnpm?.patchedDependencies) {
tryPatchWithExistingPatchFile({
patchedDep,
patchedDir: editDir,
patchedDependencies: opts.rootProjectManifest.pnpm.patchedDependencies,
lockfileDir: opts.lockfileDir ?? opts.dir ?? process.cwd(),
lockfileDir,
})
}
return `You can now edit the following folder: ${editDir}

View File

@@ -11,6 +11,7 @@ import escapeStringRegexp from 'escape-string-regexp'
import renderHelp from 'render-help'
import tempy from 'tempy'
import { writePackage } from './writePackage'
import { parseWantedDependency } from '@pnpm/parse-wanted-dependency'
export const rcOptionsTypes = cliOptionsTypes
@@ -37,7 +38,7 @@ export async function handler (opts: install.InstallCommandOptions & Pick<Config
const patchedPkgManifest = await readPackageJsonFromDir(userDir)
const pkgNameAndVersion = `${patchedPkgManifest.name}@${patchedPkgManifest.version}`
const srcDir = tempy.directory()
await writePackage(pkgNameAndVersion, srcDir, opts)
await writePackage(parseWantedDependency(pkgNameAndVersion), srcDir, opts)
const patchContent = await diffFolders(srcDir, userDir)
const patchFileName = pkgNameAndVersion.replace('/', '__')
await fs.promises.writeFile(path.join(patchesDir, `${patchFileName}.patch`), patchContent, 'utf8')

View File

@@ -4,14 +4,11 @@ import {
CreateStoreControllerOptions,
} from '@pnpm/store-connection-manager'
import { pickRegistryForPackage } from '@pnpm/pick-registry-for-package'
import { parseWantedDependency, ParseWantedDependencyResult } from '@pnpm/parse-wanted-dependency'
export { ParseWantedDependencyResult }
import type { ParseWantedDependencyResult } from '@pnpm/parse-wanted-dependency'
export type WritePackageOptions = CreateStoreControllerOptions & Pick<Config, 'registries'>
export async function writePackage (pkg: string, dest: string, opts: WritePackageOptions): Promise<ParseWantedDependencyResult> {
const dep = parseWantedDependency(pkg)
export async function writePackage (dep: ParseWantedDependencyResult, dest: string, opts: WritePackageOptions) {
const store = await createOrConnectStoreController({
...opts,
packageImportMethod: 'clone-or-copy',
@@ -28,5 +25,4 @@ export async function writePackage (pkg: string, dest: string, opts: WritePackag
filesResponse,
force: true,
})
return dep
}

View File

@@ -1,7 +1,7 @@
import fs from 'fs'
import os from 'os'
import path from 'path'
import { prepare, preparePackages } from '@pnpm/prepare'
import { prepare, preparePackages, tempDir } from '@pnpm/prepare'
import { install } from '@pnpm/plugin-commands-installation'
import { readProjects } from '@pnpm/filter-workspace-packages'
import writeYamlFile from 'write-yaml-file'
@@ -10,31 +10,50 @@ import { patch, patchCommit } from '@pnpm/plugin-commands-patching'
import { readProjectManifest } from '@pnpm/read-project-manifest'
import { REGISTRY_MOCK_PORT } from '@pnpm/registry-mock'
import { DEFAULT_OPTS } from './utils/index'
import { fixtures } from '@pnpm/test-fixtures'
import * as enquirer from 'enquirer'
jest.mock('enquirer', () => ({ prompt: jest.fn() }))
// eslint-disable-next-line
const prompt = enquirer.prompt as any
const f = fixtures(__dirname)
const basePatchOption = {
pnpmHomeDir: '',
rawConfig: {
registry: `http://localhost:${REGISTRY_MOCK_PORT}/`,
},
registries: { default: `http://localhost:${REGISTRY_MOCK_PORT}/` },
userConfig: {},
virtualStoreDir: 'node_modules/.pnpm',
}
describe('patch and commit', () => {
let defaultPatchOption: patch.PatchCommandOptions
beforeEach(() => {
beforeEach(async () => {
prepare({
dependencies: {
'is-positive': '1.0.0',
},
})
const cacheDir = path.resolve('cache')
const storeDir = path.resolve('store')
defaultPatchOption = {
...basePatchOption,
cacheDir,
dir: process.cwd(),
pnpmHomeDir: '',
rawConfig: {
registry: `http://localhost:${REGISTRY_MOCK_PORT}/`,
},
registries: { default: `http://localhost:${REGISTRY_MOCK_PORT}/` },
storeDir,
userConfig: {},
}
await install.handler({
...DEFAULT_OPTS,
cacheDir,
storeDir,
dir: process.cwd(),
saveLockfile: true,
})
})
test('patch and commit', async () => {
@@ -55,6 +74,8 @@ describe('patch and commit', () => {
await patchCommit.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
frozenLockfile: false,
fixLockfile: true,
}, [patchDir])
const { manifest } = await readProjectManifest(process.cwd())
@@ -84,6 +105,8 @@ describe('patch and commit', () => {
await patchCommit.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
frozenLockfile: false,
fixLockfile: true,
}, [patchDir])
expect(fs.readFileSync('node_modules/is-positive/index.js', 'utf8')).toContain('// test patching')
@@ -111,6 +134,8 @@ describe('patch and commit', () => {
await patchCommit.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
frozenLockfile: false,
fixLockfile: true,
}, [patchDir])
expect(fs.readFileSync('node_modules/is-positive/index.js', 'utf8')).toContain('// test patching')
@@ -126,6 +151,8 @@ describe('patch and commit', () => {
await patchCommit.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
frozenLockfile: false,
fixLockfile: true,
}, [patchDir])
const { manifest } = await readProjectManifest(process.cwd())
@@ -171,6 +198,8 @@ describe('patch and commit', () => {
await patchCommit.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
frozenLockfile: false,
fixLockfile: true,
}, [patchDir])
const { manifest } = await readProjectManifest(process.cwd())
@@ -187,12 +216,101 @@ describe('patch and commit', () => {
expect(fs.existsSync(path.join(patchDir, 'license'))).toBe(true)
expect(fs.readFileSync(path.join(patchDir, 'index.js'), 'utf8')).not.toContain('// test patching')
})
test('patch throw an error if no package specified', async () => {
await expect(() => patch.handler({ ...defaultPatchOption }, []))
.rejects.toThrow('`pnpm patch` requires the package name')
})
test('should throw an error if no installed versions found for patched package', async () => {
await expect(() => patch.handler(defaultPatchOption, ['chalk']))
.rejects.toThrow(`Can not find chalk in project ${process.cwd()}, did you forget to install chalk?`)
})
test('should throw an error if no preferred versions found for patched package', async () => {
await expect(() => patch.handler(defaultPatchOption, ['is-positive@2.0.0']))
.rejects.toThrow(`Can not find is-positive@2.0.0 in project ${process.cwd()}, you can specify currently installed version: 1.0.0.`)
})
test('patch package with installed version', async () => {
const output = await patch.handler(defaultPatchOption, ['is-positive@1'])
const patchDir = getPatchDirFromPatchOutput(output)
const tempDir = os.tmpdir()
expect(patchDir).toContain(tempDir)
expect(fs.existsSync(patchDir)).toBe(true)
expect(JSON.parse(fs.readFileSync(path.join(patchDir, 'package.json'), 'utf8')).version).toBe('1.0.0')
})
})
describe('prompt to choose version', () => {
let defaultPatchOption: patch.PatchCommandOptions
let cacheDir: string
let storeDir: string
beforeEach(() => {
prepare({
dependencies: {
ava: '5.2.0',
chalk: '4.1.2',
},
})
cacheDir = path.resolve('cache')
storeDir = path.resolve('store')
defaultPatchOption = {
...basePatchOption,
cacheDir,
dir: process.cwd(),
storeDir,
}
})
test('prompt to choose version if multiple version founded for patched package', async () => {
await install.handler({
...DEFAULT_OPTS,
cacheDir,
storeDir,
dir: process.cwd(),
saveLockfile: true,
})
prompt.mockResolvedValue({
version: '5.2.0',
})
const output = await patch.handler(defaultPatchOption, ['chalk'])
expect(prompt.mock.calls[0][0].choices).toEqual(expect.arrayContaining(['5.2.0', '4.1.2']))
prompt.mockClear()
const patchDir = getPatchDirFromPatchOutput(output)
const tempDir = os.tmpdir()
expect(patchDir).toContain(tempDir)
expect(fs.existsSync(patchDir)).toBe(true)
expect(JSON.parse(fs.readFileSync(path.join(patchDir, 'package.json'), 'utf8')).version).toBe('5.2.0')
expect(fs.existsSync(path.join(patchDir, 'source/index.js'))).toBe(true)
fs.appendFileSync(path.join(patchDir, 'source/index.js'), '// test patching', 'utf8')
await patchCommit.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
frozenLockfile: false,
fixLockfile: true,
}, [patchDir])
const { manifest } = await readProjectManifest(process.cwd())
expect(manifest.pnpm?.patchedDependencies).toStrictEqual({
'chalk@5.2.0': 'patches/chalk@5.2.0.patch',
})
const patchContent = fs.readFileSync('patches/chalk@5.2.0.patch', 'utf8')
expect(patchContent).toContain('diff --git')
expect(patchContent).toContain('// test patching')
expect(fs.readFileSync('node_modules/.pnpm/ava@5.2.0/node_modules/chalk/source/index.js', 'utf8')).toContain('// test patching')
})
})
describe('patching should work when there is a no EOL in the patched file', () => {
let defaultPatchOption: patch.PatchCommandOptions
beforeEach(() => {
beforeEach(async () => {
prepare({
dependencies: {
'safe-execa': '0.1.2',
@@ -203,16 +321,19 @@ describe('patching should work when there is a no EOL in the patched file', () =
const storeDir = path.resolve('store')
defaultPatchOption = {
...basePatchOption,
cacheDir,
dir: process.cwd(),
pnpmHomeDir: '',
rawConfig: {
registry: `http://localhost:${REGISTRY_MOCK_PORT}/`,
},
registries: { default: `http://localhost:${REGISTRY_MOCK_PORT}/` },
storeDir,
userConfig: {},
}
await install.handler({
...DEFAULT_OPTS,
cacheDir,
storeDir,
dir: process.cwd(),
saveLockfile: true,
})
})
it('should work when adding content on a newline', async () => {
const output = await patch.handler(defaultPatchOption, ['safe-execa@0.1.2'])
@@ -228,6 +349,8 @@ describe('patching should work when there is a no EOL in the patched file', () =
await patchCommit.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
frozenLockfile: false,
fixLockfile: true,
}, [userPatchDir])
const { manifest } = await readProjectManifest(process.cwd())
@@ -254,6 +377,8 @@ describe('patching should work when there is a no EOL in the patched file', () =
await patchCommit.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
frozenLockfile: false,
fixLockfile: true,
}, [userPatchDir])
const { manifest } = await readProjectManifest(process.cwd())
@@ -300,15 +425,10 @@ describe('patch and commit in workspaces', () => {
storeDir = path.resolve('store')
defaultPatchOption = {
...basePatchOption,
cacheDir,
dir: process.cwd(),
pnpmHomeDir: '',
rawConfig: {
registry: `http://localhost:${REGISTRY_MOCK_PORT}/`,
},
registries: { default: `http://localhost:${REGISTRY_MOCK_PORT}/` },
storeDir,
userConfig: {},
}
})
@@ -322,11 +442,11 @@ describe('patch and commit in workspaces', () => {
allProjects,
allProjectsGraph,
dir: process.cwd(),
lockfileDir: process.cwd(),
selectedProjectsGraph,
workspaceDir: process.cwd(),
saveLockfile: true,
})
const output = await patch.handler(defaultPatchOption, ['is-positive@1.0.0'])
const patchDir = getPatchDirFromPatchOutput(output)
const tempDir = os.tmpdir()
@@ -350,6 +470,8 @@ describe('patch and commit in workspaces', () => {
lockfileDir: process.cwd(),
workspaceDir: process.cwd(),
saveLockfile: true,
frozenLockfile: false,
fixLockfile: true,
}, [patchDir])
const { manifest } = await readProjectManifest(process.cwd())
@@ -366,6 +488,68 @@ describe('patch and commit in workspaces', () => {
})
})
describe('patch with custom modules-dir and virtual-store-dir', () => {
const customModulesDirFixture = tempDir()
f.copy('custom-modules-dir', customModulesDirFixture)
const cacheDir = path.resolve(customModulesDirFixture, 'cache')
const storeDir = path.resolve(customModulesDirFixture, 'store')
const defaultPatchOption = {
...basePatchOption,
cacheDir,
dir: customModulesDirFixture,
storeDir,
modulesDir: 'fake_modules',
virtualStoreDir: 'fake_modules/.fake_store',
}
test('should work with custom modules-dir and virtual-store-dir', async () => {
const manifest = fs.readFileSync(path.join(customModulesDirFixture, 'package.json'), 'utf8')
const lockfileYaml = fs.readFileSync(path.join(customModulesDirFixture, 'pnpm-lock.yaml'), 'utf8')
const { allProjects, allProjectsGraph, selectedProjectsGraph } = await readProjects(customModulesDirFixture, [])
await install.handler({
...DEFAULT_OPTS,
cacheDir,
storeDir,
dir: customModulesDirFixture,
lockfileDir: customModulesDirFixture,
allProjects,
allProjectsGraph,
selectedProjectsGraph,
workspaceDir: customModulesDirFixture,
saveLockfile: true,
modulesDir: 'fake_modules',
virtualStoreDir: 'fake_modules/.fake_store',
})
const output = await patch.handler(defaultPatchOption, ['is-positive@1'])
const patchDir = getPatchDirFromPatchOutput(output)
const tempDir = os.tmpdir()
expect(patchDir).toContain(tempDir)
expect(fs.existsSync(patchDir)).toBe(true)
expect(JSON.parse(fs.readFileSync(path.join(patchDir, 'package.json'), 'utf8')).version).toBe('1.0.0')
fs.appendFileSync(path.join(patchDir, 'index.js'), '// test patching', 'utf8')
await patchCommit.handler({
...DEFAULT_OPTS,
dir: customModulesDirFixture,
saveLockfile: true,
frozenLockfile: false,
fixLockfile: true,
allProjects,
allProjectsGraph,
selectedProjectsGraph,
modulesDir: 'fake_modules',
virtualStoreDir: 'fake_modules/.fake_store',
lockfileDir: customModulesDirFixture,
workspaceDir: customModulesDirFixture,
}, [patchDir])
expect(fs.readFileSync(path.join(customModulesDirFixture, 'packages/bar/fake_modules/is-positive/index.js'), 'utf8')).toContain('// test patching')
// restore package.json and package-lock.yaml
fs.writeFileSync(path.join(customModulesDirFixture, 'package.json'), manifest, 'utf8')
fs.writeFileSync(path.join(customModulesDirFixture, 'pnpm-lock.yaml'), lockfileYaml, 'utf8')
})
})
function getPatchDirFromPatchOutput (output: string) {
const [firstLine] = output.split('\n')
return firstLine.substring(firstLine.indexOf(':') + 1).trim()

View File

@@ -12,6 +12,9 @@
{
"path": "../../__utils__/prepare"
},
{
"path": "../../__utils__/test-fixtures"
},
{
"path": "../../cli/cli-utils"
},
@@ -21,12 +24,24 @@
{
"path": "../../config/pick-registry-for-package"
},
{
"path": "../../lockfile/lockfile-file"
},
{
"path": "../../lockfile/lockfile-utils"
},
{
"path": "../../packages/constants"
},
{
"path": "../../packages/error"
},
{
"path": "../../packages/parse-wanted-dependency"
},
{
"path": "../../pkg-manager/modules-yaml"
},
{
"path": "../../pkg-manager/plugin-commands-installation"
},

View File

@@ -1,5 +1,14 @@
# @pnpm/client
## 9.1.5
### Patch Changes
- @pnpm/tarball-fetcher@14.1.4
- @pnpm/directory-fetcher@5.1.6
- @pnpm/default-resolver@17.0.11
- @pnpm/git-fetcher@8.0.2
## 9.1.4
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/client",
"version": "9.1.4",
"version": "9.1.5",
"description": "Creates the package resolve and fetch functions",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@@ -1,5 +1,24 @@
# @pnpm/core
## 8.0.2
### Patch Changes
- Updated dependencies [787c43dcc]
- @pnpm/lockfile-file@7.0.6
- @pnpm/package-requester@20.1.7
- @pnpm/read-project-manifest@4.1.4
- @pnpm/lockfile-to-pnp@2.0.14
- @pnpm/get-context@8.2.4
- @pnpm/headless@19.5.2
- @pnpm/link-bins@8.0.9
- @pnpm/resolve-dependencies@30.0.2
- @pnpm/lifecycle@14.1.7
- @pnpm/build-modules@10.1.7
- @pnpm/hoist@7.0.16
- @pnpm/symlink-dependency@6.0.3
- @pnpm/crypto.base32-hash@1.0.1
## 8.0.1
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "@pnpm/core",
"description": "Fast, disk space efficient installation engine",
"version": "8.0.1",
"version": "8.0.2",
"bugs": {
"url": "https://github.com/pnpm/pnpm/issues"
},

View File

@@ -1,5 +1,13 @@
# @pnpm/get-context
## 8.2.4
### Patch Changes
- Updated dependencies [787c43dcc]
- @pnpm/lockfile-file@7.0.6
- @pnpm/read-projects-context@7.0.12
## 8.2.3
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@pnpm/get-context",
"version": "8.2.3",
"version": "8.2.4",
"description": "Gets context information about a project",
"main": "lib/index.js",
"types": "lib/index.d.ts",

Some files were not shown because too many files have changed in this diff Show More