@pnpm/patching.commands 1000.3.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +13 -0
- package/lib/getEditDirPath.d.ts +5 -0
- package/lib/getEditDirPath.js +16 -0
- package/lib/getPatchedDependency.d.ts +21 -0
- package/lib/getPatchedDependency.js +93 -0
- package/lib/index.d.ts +6 -0
- package/lib/index.js +7 -0
- package/lib/patch.d.ts +16 -0
- package/lib/patch.js +113 -0
- package/lib/patchCommit.d.ts +9 -0
- package/lib/patchCommit.js +216 -0
- package/lib/patchRemove.d.ts +8 -0
- package/lib/patchRemove.js +74 -0
- package/lib/stateFile.d.ts +18 -0
- package/lib/stateFile.js +47 -0
- package/lib/updatePatchedDependencies.d.ts +2 -0
- package/lib/updatePatchedDependencies.js +19 -0
- package/lib/writePackage.d.ts +5 -0
- package/lib/writePackage.js +19 -0
- package/package.json +93 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2015-2016 Rico Sta. Cruz and other contributors
|
|
4
|
+
Copyright (c) 2016-2026 Zoltan Kochan and other contributors
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ParseWantedDependencyResult } from '@pnpm/resolving.parse-wanted-dependency';
|
|
2
|
+
export interface GetEditDirOptions {
|
|
3
|
+
modulesDir: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function getEditDirPath(param: string, patchedDep: ParseWantedDependencyResult, opts: GetEditDirOptions): string;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
export function getEditDirPath(param, patchedDep, opts) {
|
|
3
|
+
const editDirName = getEditDirNameFromParsedDep(patchedDep) ?? param;
|
|
4
|
+
return path.join(opts.modulesDir, '.pnpm_patches', editDirName);
|
|
5
|
+
}
|
|
6
|
+
function getEditDirNameFromParsedDep(patchedDep) {
|
|
7
|
+
if (patchedDep.alias && patchedDep.bareSpecifier) {
|
|
8
|
+
const bareSpecifier = patchedDep.bareSpecifier.replace(/[\\/:*?"<>|]+/g, '+');
|
|
9
|
+
return `${patchedDep.alias}@${bareSpecifier}`;
|
|
10
|
+
}
|
|
11
|
+
if (patchedDep.alias) {
|
|
12
|
+
return patchedDep.alias;
|
|
13
|
+
}
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=getEditDirPath.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Config } from '@pnpm/config.reader';
|
|
2
|
+
import { type ParseWantedDependencyResult } from '@pnpm/resolving.parse-wanted-dependency';
|
|
3
|
+
export type GetPatchedDependencyOptions = {
|
|
4
|
+
lockfileDir: string;
|
|
5
|
+
} & Pick<Config, 'virtualStoreDir' | 'modulesDir'>;
|
|
6
|
+
export type GetPatchedDependencyResult = ParseWantedDependencyResult & {
|
|
7
|
+
applyToAll: boolean;
|
|
8
|
+
};
|
|
9
|
+
export declare function getPatchedDependency(rawDependency: string, opts: GetPatchedDependencyOptions): Promise<GetPatchedDependencyResult>;
|
|
10
|
+
export declare function isPkgPrNewUrl(url: string): boolean;
|
|
11
|
+
export interface LockfileVersion {
|
|
12
|
+
gitTarballUrl?: string;
|
|
13
|
+
name: string;
|
|
14
|
+
peerDepGraphHash?: string;
|
|
15
|
+
version: string;
|
|
16
|
+
}
|
|
17
|
+
export interface LockfileVersionsList {
|
|
18
|
+
versions: LockfileVersion[];
|
|
19
|
+
preferredVersions: LockfileVersion[];
|
|
20
|
+
}
|
|
21
|
+
export declare function getVersionsFromLockfile(dep: ParseWantedDependencyResult, opts: GetPatchedDependencyOptions): Promise<LockfileVersionsList>;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { PnpmError } from '@pnpm/error';
|
|
3
|
+
import { isGitHostedPkgUrl } from '@pnpm/fetching.pick-fetcher';
|
|
4
|
+
import { readCurrentLockfile } from '@pnpm/lockfile.fs';
|
|
5
|
+
import { nameVerFromPkgSnapshot } from '@pnpm/lockfile.utils';
|
|
6
|
+
import { parseWantedDependency } from '@pnpm/resolving.parse-wanted-dependency';
|
|
7
|
+
import enquirer from 'enquirer';
|
|
8
|
+
import { realpathMissing } from 'realpath-missing';
|
|
9
|
+
import semver from 'semver';
|
|
10
|
+
export async function getPatchedDependency(rawDependency, opts) {
|
|
11
|
+
const dep = parseWantedDependency(rawDependency);
|
|
12
|
+
const { versions, preferredVersions } = await getVersionsFromLockfile(dep, opts);
|
|
13
|
+
if (!preferredVersions.length) {
|
|
14
|
+
throw new PnpmError('PATCH_VERSION_NOT_FOUND', `Can not find ${rawDependency} in project ${opts.lockfileDir}, ${versions.length ? `you can specify currently installed version: ${versions.map(({ version }) => version).join(', ')}.` : `did you forget to install ${rawDependency}?`}`);
|
|
15
|
+
}
|
|
16
|
+
dep.alias = dep.alias ?? rawDependency;
|
|
17
|
+
if (preferredVersions.length > 1) {
|
|
18
|
+
const { version, applyToAll } = await enquirer.prompt([{
|
|
19
|
+
type: 'select',
|
|
20
|
+
name: 'version',
|
|
21
|
+
message: 'Choose which version to patch',
|
|
22
|
+
choices: preferredVersions.map(preferred => ({
|
|
23
|
+
name: preferred.version,
|
|
24
|
+
message: preferred.version,
|
|
25
|
+
value: preferred.gitTarballUrl ?? preferred.version,
|
|
26
|
+
hint: preferred.gitTarballUrl ? 'Git Hosted' : undefined,
|
|
27
|
+
})),
|
|
28
|
+
result(selected) {
|
|
29
|
+
const selectedVersion = preferredVersions.find(preferred => preferred.version === selected);
|
|
30
|
+
return selectedVersion.gitTarballUrl ?? selected;
|
|
31
|
+
},
|
|
32
|
+
}, {
|
|
33
|
+
type: 'confirm',
|
|
34
|
+
name: 'applyToAll',
|
|
35
|
+
message: 'Apply this patch to all versions?',
|
|
36
|
+
}]);
|
|
37
|
+
return {
|
|
38
|
+
...dep,
|
|
39
|
+
applyToAll,
|
|
40
|
+
bareSpecifier: version,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
const preferred = preferredVersions[0];
|
|
45
|
+
if (preferred.gitTarballUrl) {
|
|
46
|
+
return {
|
|
47
|
+
...opts,
|
|
48
|
+
applyToAll: false,
|
|
49
|
+
bareSpecifier: preferred.gitTarballUrl,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
...dep,
|
|
54
|
+
applyToAll: !dep.bareSpecifier,
|
|
55
|
+
bareSpecifier: preferred.version,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// https://github.com/stackblitz-labs/pkg.pr.new
|
|
60
|
+
// With pkg.pr.new, each of your commits and pull requests will trigger an instant preview release without publishing anything to NPM.
|
|
61
|
+
// This enables users to access features and bug-fixes without the need to wait for release cycles using npm or pull request merges.
|
|
62
|
+
// When a package is installed via pkg.pr.new and has never been published to npm,
|
|
63
|
+
// the version or name obtained is incorrect, and an error will occur when patching. We can treat it as a tarball url.
|
|
64
|
+
export function isPkgPrNewUrl(url) {
|
|
65
|
+
return url.startsWith('https://pkg.pr.new/');
|
|
66
|
+
}
|
|
67
|
+
export async function getVersionsFromLockfile(dep, opts) {
|
|
68
|
+
const modulesDir = await realpathMissing(path.join(opts.lockfileDir, opts.modulesDir ?? 'node_modules'));
|
|
69
|
+
const lockfile = await readCurrentLockfile(path.join(modulesDir, '.pnpm'), {
|
|
70
|
+
ignoreIncompatible: true,
|
|
71
|
+
}) ?? null;
|
|
72
|
+
if (!lockfile) {
|
|
73
|
+
throw new PnpmError('PATCH_NO_LOCKFILE', 'The modules directory is not ready for patching', {
|
|
74
|
+
hint: 'Run pnpm install first',
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
const pkgName = dep.alias && dep.bareSpecifier ? dep.alias : (dep.bareSpecifier ?? dep.alias);
|
|
78
|
+
const versions = Object.entries(lockfile.packages ?? {})
|
|
79
|
+
.map(([depPath, pkgSnapshot]) => {
|
|
80
|
+
const tarball = pkgSnapshot.resolution?.tarball ?? '';
|
|
81
|
+
return {
|
|
82
|
+
...nameVerFromPkgSnapshot(depPath, pkgSnapshot),
|
|
83
|
+
gitTarballUrl: (isGitHostedPkgUrl(tarball) || isPkgPrNewUrl(tarball)) ? tarball : undefined,
|
|
84
|
+
};
|
|
85
|
+
})
|
|
86
|
+
.filter(({ name }) => name === pkgName)
|
|
87
|
+
.sort((v1, v2) => semver.compare(v1.version, v2.version));
|
|
88
|
+
return {
|
|
89
|
+
versions,
|
|
90
|
+
preferredVersions: versions.filter(({ version }) => dep.alias && dep.bareSpecifier ? semver.satisfies(version, dep.bareSpecifier) : true),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=getPatchedDependency.js.map
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import * as patch from './patch.js';
|
|
2
|
+
import * as patchCommit from './patchCommit.js';
|
|
3
|
+
import * as patchRemove from './patchRemove.js';
|
|
4
|
+
export { type PatchCommandOptions } from './patch.js';
|
|
5
|
+
export { type PatchRemoveCommandOptions } from './patchRemove.js';
|
|
6
|
+
export { patch, patchCommit, patchRemove };
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as patch from './patch.js';
|
|
2
|
+
import * as patchCommit from './patchCommit.js';
|
|
3
|
+
import * as patchRemove from './patchRemove.js';
|
|
4
|
+
export {} from './patch.js';
|
|
5
|
+
export {} from './patchRemove.js';
|
|
6
|
+
export { patch, patchCommit, patchRemove };
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
package/lib/patch.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type Config } from '@pnpm/config.reader';
|
|
2
|
+
import type { LogBase } from '@pnpm/logger';
|
|
3
|
+
import type { CreateStoreControllerOptions } from '@pnpm/store.connection-manager';
|
|
4
|
+
export declare function rcOptionsTypes(): Record<string, unknown>;
|
|
5
|
+
export declare function cliOptionsTypes(): Record<string, unknown>;
|
|
6
|
+
export declare const shorthands: {
|
|
7
|
+
d: string;
|
|
8
|
+
};
|
|
9
|
+
export declare const commandNames: string[];
|
|
10
|
+
export declare function help(): string;
|
|
11
|
+
export type PatchCommandOptions = Pick<Config, 'dir' | 'patchedDependencies' | 'registries' | 'tag' | 'storeDir' | 'rootProjectManifest' | 'lockfileDir' | 'modulesDir' | 'virtualStoreDir' | 'sharedWorkspaceLockfile'> & CreateStoreControllerOptions & {
|
|
12
|
+
editDir?: string;
|
|
13
|
+
reporter?: (logObj: LogBase) => void;
|
|
14
|
+
ignoreExisting?: boolean;
|
|
15
|
+
};
|
|
16
|
+
export declare function handler(opts: PatchCommandOptions, params: string[]): Promise<string>;
|
package/lib/patch.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { docsUrl } from '@pnpm/cli.utils';
|
|
4
|
+
import { types as allTypes } from '@pnpm/config.reader';
|
|
5
|
+
import { PnpmError } from '@pnpm/error';
|
|
6
|
+
import { applyPatchToDir } from '@pnpm/patching.apply-patch';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import isWindows from 'is-windows';
|
|
9
|
+
import { pick } from 'ramda';
|
|
10
|
+
import { renderHelp } from 'render-help';
|
|
11
|
+
import terminalLink from 'terminal-link';
|
|
12
|
+
import { getEditDirPath } from './getEditDirPath.js';
|
|
13
|
+
import { getPatchedDependency } from './getPatchedDependency.js';
|
|
14
|
+
import { writeEditDirState } from './stateFile.js';
|
|
15
|
+
import { writePackage } from './writePackage.js';
|
|
16
|
+
export function rcOptionsTypes() {
|
|
17
|
+
return pick([], allTypes);
|
|
18
|
+
}
|
|
19
|
+
export function cliOptionsTypes() {
|
|
20
|
+
return { ...rcOptionsTypes(), 'edit-dir': String, 'ignore-existing': Boolean };
|
|
21
|
+
}
|
|
22
|
+
export const shorthands = {
|
|
23
|
+
d: '--edit-dir',
|
|
24
|
+
};
|
|
25
|
+
export const commandNames = ['patch'];
|
|
26
|
+
export function help() {
|
|
27
|
+
return renderHelp({
|
|
28
|
+
description: 'Prepare a package for patching',
|
|
29
|
+
descriptionLists: [{
|
|
30
|
+
title: 'Options',
|
|
31
|
+
list: [
|
|
32
|
+
{
|
|
33
|
+
description: 'The package that needs to be modified will be extracted to this directory',
|
|
34
|
+
name: '--edit-dir',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
description: 'Ignore existing patch files when patching',
|
|
38
|
+
name: '--ignore-existing',
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
}],
|
|
42
|
+
url: docsUrl('patch'),
|
|
43
|
+
usages: ['pnpm patch <pkg name>@<version>'],
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
export async function handler(opts, params) {
|
|
47
|
+
if (opts.editDir && fs.existsSync(opts.editDir) && fs.readdirSync(opts.editDir).length > 0) {
|
|
48
|
+
throw new PnpmError('PATCH_EDIT_DIR_EXISTS', `The target directory already exists: '${opts.editDir}'`);
|
|
49
|
+
}
|
|
50
|
+
if (!params[0]) {
|
|
51
|
+
throw new PnpmError('MISSING_PACKAGE_NAME', '`pnpm patch` requires the package name');
|
|
52
|
+
}
|
|
53
|
+
const lockfileDir = opts.lockfileDir ?? opts.dir ?? process.cwd();
|
|
54
|
+
const patchedDep = await getPatchedDependency(params[0], {
|
|
55
|
+
lockfileDir,
|
|
56
|
+
modulesDir: opts.modulesDir,
|
|
57
|
+
virtualStoreDir: opts.virtualStoreDir,
|
|
58
|
+
});
|
|
59
|
+
const quote = isWindows() ? '"' : "'";
|
|
60
|
+
const modulesDir = path.join(lockfileDir, opts.modulesDir ?? 'node_modules');
|
|
61
|
+
const editDir = opts.editDir
|
|
62
|
+
? path.resolve(opts.dir, opts.editDir)
|
|
63
|
+
: getEditDirPath(params[0], patchedDep, { modulesDir });
|
|
64
|
+
if (fs.existsSync(editDir) && fs.readdirSync(editDir).length !== 0) {
|
|
65
|
+
throw new PnpmError('EDIT_DIR_NOT_EMPTY', `The directory ${editDir} is not empty`, {
|
|
66
|
+
hint: 'Either run `pnpm patch-commit ' + quote + editDir + quote + '` to commit or delete it then run `pnpm patch` to recreate it',
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
await writePackage(patchedDep, editDir, opts);
|
|
70
|
+
writeEditDirState({
|
|
71
|
+
editDir,
|
|
72
|
+
modulesDir,
|
|
73
|
+
patchedPkg: params[0],
|
|
74
|
+
applyToAll: patchedDep.applyToAll,
|
|
75
|
+
});
|
|
76
|
+
if (!opts.ignoreExisting && opts.patchedDependencies) {
|
|
77
|
+
tryPatchWithExistingPatchFile({
|
|
78
|
+
patchedDep,
|
|
79
|
+
patchedDir: editDir,
|
|
80
|
+
patchedDependencies: opts.patchedDependencies,
|
|
81
|
+
lockfileDir,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return `Patch: You can now edit the package at:
|
|
85
|
+
|
|
86
|
+
${terminalLink(chalk.blue(editDir), 'file://' + editDir, { fallback: false })}
|
|
87
|
+
|
|
88
|
+
To commit your changes, run:
|
|
89
|
+
|
|
90
|
+
${chalk.green(`pnpm patch-commit ${quote}${editDir}${quote}`)}
|
|
91
|
+
|
|
92
|
+
`;
|
|
93
|
+
}
|
|
94
|
+
function tryPatchWithExistingPatchFile({ patchedDep: { applyToAll, alias, bareSpecifier }, patchedDir, patchedDependencies, lockfileDir, }) {
|
|
95
|
+
if (!alias)
|
|
96
|
+
return;
|
|
97
|
+
let existingPatchFile;
|
|
98
|
+
if (bareSpecifier) {
|
|
99
|
+
existingPatchFile = patchedDependencies[`${alias}@${bareSpecifier}`];
|
|
100
|
+
}
|
|
101
|
+
if (!existingPatchFile && applyToAll) {
|
|
102
|
+
existingPatchFile = patchedDependencies[alias];
|
|
103
|
+
}
|
|
104
|
+
if (!existingPatchFile) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const existingPatchFilePath = path.resolve(lockfileDir, existingPatchFile);
|
|
108
|
+
if (!fs.existsSync(existingPatchFilePath)) {
|
|
109
|
+
throw new PnpmError('PATCH_FILE_NOT_FOUND', `Unable to find patch file ${existingPatchFilePath}`);
|
|
110
|
+
}
|
|
111
|
+
applyPatchToDir({ patchedDir, patchFilePath: existingPatchFilePath });
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=patch.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Config } from '@pnpm/config.reader';
|
|
2
|
+
import { install } from '@pnpm/installing.commands';
|
|
3
|
+
export declare const rcOptionsTypes: typeof cliOptionsTypes;
|
|
4
|
+
export declare function cliOptionsTypes(): Record<string, unknown>;
|
|
5
|
+
export declare const commandNames: string[];
|
|
6
|
+
export declare function help(): string;
|
|
7
|
+
type PatchCommitCommandOptions = install.InstallCommandOptions & Pick<Config, 'patchesDir' | 'rootProjectManifest' | 'rootProjectManifestDir' | 'patchedDependencies'>;
|
|
8
|
+
export declare function handler(opts: PatchCommitCommandOptions, params: string[]): Promise<string | undefined>;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { docsUrl } from '@pnpm/cli.utils';
|
|
4
|
+
import { types as allTypes } from '@pnpm/config.reader';
|
|
5
|
+
import { createShortHash } from '@pnpm/crypto.hash';
|
|
6
|
+
import { PnpmError } from '@pnpm/error';
|
|
7
|
+
import { packlist } from '@pnpm/fs.packlist';
|
|
8
|
+
import { install } from '@pnpm/installing.commands';
|
|
9
|
+
import { globalWarn } from '@pnpm/logger';
|
|
10
|
+
import { readPackageJsonFromDir } from '@pnpm/pkg-manifest.reader';
|
|
11
|
+
import { parseWantedDependency } from '@pnpm/resolving.parse-wanted-dependency';
|
|
12
|
+
import { getStorePath } from '@pnpm/store.path';
|
|
13
|
+
import escapeStringRegexp from 'escape-string-regexp';
|
|
14
|
+
import { makeEmptyDir } from 'make-empty-dir';
|
|
15
|
+
import normalizePath from 'normalize-path';
|
|
16
|
+
import { equals, pick } from 'ramda';
|
|
17
|
+
import { renderHelp } from 'render-help';
|
|
18
|
+
import { safeExeca as execa } from 'safe-execa';
|
|
19
|
+
import { glob } from 'tinyglobby';
|
|
20
|
+
import { getVersionsFromLockfile } from './getPatchedDependency.js';
|
|
21
|
+
import { readEditDirState } from './stateFile.js';
|
|
22
|
+
import { updatePatchedDependencies } from './updatePatchedDependencies.js';
|
|
23
|
+
import { writePackage } from './writePackage.js';
|
|
24
|
+
export const rcOptionsTypes = cliOptionsTypes;
|
|
25
|
+
export function cliOptionsTypes() {
|
|
26
|
+
return pick(['patches-dir'], allTypes);
|
|
27
|
+
}
|
|
28
|
+
export const commandNames = ['patch-commit'];
|
|
29
|
+
export function help() {
|
|
30
|
+
return renderHelp({
|
|
31
|
+
description: 'Generate a patch out of a directory',
|
|
32
|
+
descriptionLists: [{
|
|
33
|
+
title: 'Options',
|
|
34
|
+
list: [
|
|
35
|
+
{
|
|
36
|
+
description: 'The generated patch file will be saved to this directory',
|
|
37
|
+
name: '--patches-dir',
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
}],
|
|
41
|
+
url: docsUrl('patch-commit'),
|
|
42
|
+
usages: ['pnpm patch-commit <patchDir>'],
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
export async function handler(opts, params) {
|
|
46
|
+
const userDir = params[0];
|
|
47
|
+
const lockfileDir = (opts.lockfileDir ?? opts.dir ?? process.cwd());
|
|
48
|
+
const patchesDirName = normalizePath(path.normalize(opts.patchesDir ?? 'patches'));
|
|
49
|
+
const patchesDir = path.join(lockfileDir, patchesDirName);
|
|
50
|
+
const patchedPkgManifest = await readPackageJsonFromDir(userDir);
|
|
51
|
+
const editDir = path.resolve(opts.dir, userDir);
|
|
52
|
+
const stateValue = readEditDirState({
|
|
53
|
+
editDir,
|
|
54
|
+
modulesDir: path.join(lockfileDir, opts.modulesDir ?? 'node_modules'),
|
|
55
|
+
});
|
|
56
|
+
if (!stateValue) {
|
|
57
|
+
throw new PnpmError('INVALID_PATCH_DIR', `${userDir} is not a valid patch directory`, {
|
|
58
|
+
hint: 'A valid patch directory should be created by `pnpm patch`',
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
const { applyToAll } = stateValue;
|
|
62
|
+
const nameAndVersion = `${patchedPkgManifest.name}@${patchedPkgManifest.version}`;
|
|
63
|
+
const patchKey = applyToAll ? patchedPkgManifest.name : nameAndVersion;
|
|
64
|
+
let gitTarballUrl;
|
|
65
|
+
if (!applyToAll) {
|
|
66
|
+
gitTarballUrl = await getGitTarballUrlFromLockfile({
|
|
67
|
+
alias: patchedPkgManifest.name,
|
|
68
|
+
bareSpecifier: patchedPkgManifest.version || undefined,
|
|
69
|
+
}, {
|
|
70
|
+
lockfileDir,
|
|
71
|
+
modulesDir: opts.modulesDir,
|
|
72
|
+
virtualStoreDir: opts.virtualStoreDir,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
const patchedPkg = parseWantedDependency(gitTarballUrl ? `${patchedPkgManifest.name}@${gitTarballUrl}` : nameAndVersion);
|
|
76
|
+
const patchedPkgDir = await preparePkgFilesForDiff(userDir);
|
|
77
|
+
const patchContent = await getPatchContent({
|
|
78
|
+
patchedPkg,
|
|
79
|
+
patchedPkgDir,
|
|
80
|
+
tmpName: createShortHash(editDir),
|
|
81
|
+
}, opts);
|
|
82
|
+
if (patchedPkgDir !== userDir) {
|
|
83
|
+
fs.rmSync(patchedPkgDir, { recursive: true });
|
|
84
|
+
}
|
|
85
|
+
if (!patchContent.length) {
|
|
86
|
+
return `No changes were found to the following directory: ${userDir}`;
|
|
87
|
+
}
|
|
88
|
+
await fs.promises.mkdir(patchesDir, { recursive: true });
|
|
89
|
+
const patchFileName = patchKey.replace('/', '__');
|
|
90
|
+
await fs.promises.writeFile(path.join(patchesDir, `${patchFileName}.patch`), patchContent, 'utf8');
|
|
91
|
+
const patchedDependencies = {
|
|
92
|
+
...opts.patchedDependencies,
|
|
93
|
+
[patchKey]: `${patchesDirName}/${patchFileName}.patch`,
|
|
94
|
+
};
|
|
95
|
+
await updatePatchedDependencies(patchedDependencies, {
|
|
96
|
+
...opts,
|
|
97
|
+
workspaceDir: opts.workspaceDir ?? opts.rootProjectManifestDir,
|
|
98
|
+
});
|
|
99
|
+
return install.handler({
|
|
100
|
+
...opts,
|
|
101
|
+
patchedDependencies,
|
|
102
|
+
rawLocalConfig: {
|
|
103
|
+
...opts.rawLocalConfig,
|
|
104
|
+
'frozen-lockfile': false,
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
async function getPatchContent(ctx, opts) {
|
|
109
|
+
const storeDir = await getStorePath({
|
|
110
|
+
pkgRoot: opts.dir,
|
|
111
|
+
storePath: opts.storeDir,
|
|
112
|
+
pnpmHomeDir: opts.pnpmHomeDir,
|
|
113
|
+
});
|
|
114
|
+
const srcDir = path.join(storeDir, 'tmp', 'patch-commit', ctx.tmpName);
|
|
115
|
+
await writePackage(ctx.patchedPkg, srcDir, opts);
|
|
116
|
+
const patchContent = await diffFolders(srcDir, ctx.patchedPkgDir);
|
|
117
|
+
try {
|
|
118
|
+
fs.rmSync(srcDir, { recursive: true });
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
globalWarn(`Failed to clean up temporary directory at ${srcDir} with error: ${String(error)}`);
|
|
122
|
+
}
|
|
123
|
+
return patchContent;
|
|
124
|
+
}
|
|
125
|
+
async function diffFolders(folderA, folderB) {
|
|
126
|
+
const folderAN = folderA.replace(/\\/g, '/');
|
|
127
|
+
const folderBN = folderB.replace(/\\/g, '/');
|
|
128
|
+
let stdout;
|
|
129
|
+
let stderr;
|
|
130
|
+
try {
|
|
131
|
+
const result = await execa('git', ['-c', 'core.safecrlf=false', 'diff', '--src-prefix=a/', '--dst-prefix=b/', '--ignore-cr-at-eol', '--irreversible-delete', '--full-index', '--no-index', '--text', '--no-ext-diff', '--no-color', folderAN, folderBN], {
|
|
132
|
+
cwd: process.cwd(),
|
|
133
|
+
env: {
|
|
134
|
+
...process.env,
|
|
135
|
+
// #region Predictable output
|
|
136
|
+
// These variables aim to ignore the global git config so we get predictable output
|
|
137
|
+
// https://git-scm.com/docs/git#Documentation/git.txt-codeGITCONFIGNOSYSTEMcode
|
|
138
|
+
GIT_CONFIG_NOSYSTEM: '1',
|
|
139
|
+
// Redirect the global git config to /dev/null instead of setting
|
|
140
|
+
// HOME to an empty string. An empty HOME causes git to resolve '~' as
|
|
141
|
+
// '/' (root), which triggers a "Permission denied" warning when git
|
|
142
|
+
// tries to access '/.config/git/attributes', making pnpm throw an
|
|
143
|
+
// error because any stderr output is treated as a failure.
|
|
144
|
+
// We do not set XDG_CONFIG_HOME to avoid the same issue: an empty
|
|
145
|
+
// value would make git resolve paths like /git/config and /git/attributes.
|
|
146
|
+
// We use '/dev/null' literally instead of os.devNull because on Windows
|
|
147
|
+
// os.devNull is '\\.\nul', which git cannot open as a config file path
|
|
148
|
+
// (fatal: unable to access '\\.\nul': Invalid argument). Git for Windows
|
|
149
|
+
// translates '/dev/null' correctly via its MSYS2 layer.
|
|
150
|
+
GIT_CONFIG_GLOBAL: '/dev/null',
|
|
151
|
+
// #endregion
|
|
152
|
+
},
|
|
153
|
+
stripFinalNewline: false,
|
|
154
|
+
});
|
|
155
|
+
stdout = result.stdout;
|
|
156
|
+
stderr = result.stderr;
|
|
157
|
+
}
|
|
158
|
+
catch (err) { // eslint-disable-line
|
|
159
|
+
stdout = err.stdout;
|
|
160
|
+
stderr = err.stderr;
|
|
161
|
+
}
|
|
162
|
+
// we cannot rely on exit code, because --no-index implies --exit-code
|
|
163
|
+
// i.e. git diff will exit with 1 if there were differences
|
|
164
|
+
if (stderr.length > 0)
|
|
165
|
+
throw new Error(`Unable to diff directories. Make sure you have a recent version of 'git' available in PATH.\nThe following error was reported by 'git':\n${stderr}`);
|
|
166
|
+
return stdout
|
|
167
|
+
.replace(new RegExp(`(a|b)(${escapeStringRegexp(`/${removeTrailingAndLeadingSlash(folderAN)}/`)})`, 'g'), '$1/')
|
|
168
|
+
.replace(new RegExp(`(a|b)${escapeStringRegexp(`/${removeTrailingAndLeadingSlash(folderBN)}/`)}`, 'g'), '$1/')
|
|
169
|
+
.replace(new RegExp(escapeStringRegexp(`${folderAN}/`), 'g'), '')
|
|
170
|
+
.replace(new RegExp(escapeStringRegexp(`${folderBN}/`), 'g'), '')
|
|
171
|
+
.replace(/\n\\n$/, '\n')
|
|
172
|
+
.replace(/^diff --git a\/.*\.DS_Store b\/.*\.DS_Store[\s\S]+?(?=^diff --git)/gm, '')
|
|
173
|
+
.replace(/^diff --git a\/.*\.DS_Store b\/.*\.DS_Store[\s\S]*$/gm, '');
|
|
174
|
+
}
|
|
175
|
+
function removeTrailingAndLeadingSlash(p) {
|
|
176
|
+
if (p[0] === '/' || p.endsWith('/')) {
|
|
177
|
+
return p.replace(/^\/|\/$/g, '');
|
|
178
|
+
}
|
|
179
|
+
return p;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Link files from the source directory to a new temporary directory,
|
|
183
|
+
* but only if not all files in the source directory should be included in the package.
|
|
184
|
+
* If all files should be included, return the original source directory without creating any links.
|
|
185
|
+
* This is required in order for the diff to not include files that are not part of the package.
|
|
186
|
+
*/
|
|
187
|
+
async function preparePkgFilesForDiff(src) {
|
|
188
|
+
const files = Array.from(new Set((await packlist(src)).map((f) => path.join(f))));
|
|
189
|
+
// If there are no extra files in the source directories, then there is no reason
|
|
190
|
+
// to copy.
|
|
191
|
+
if (await areAllFilesInPkg(files, src)) {
|
|
192
|
+
return src;
|
|
193
|
+
}
|
|
194
|
+
const dest = `${src}_tmp`;
|
|
195
|
+
await makeEmptyDir(dest);
|
|
196
|
+
await Promise.all(files.map(async (file) => {
|
|
197
|
+
const srcFile = path.join(src, file);
|
|
198
|
+
const destFile = path.join(dest, file);
|
|
199
|
+
const destDir = path.dirname(destFile);
|
|
200
|
+
await fs.promises.mkdir(destDir, { recursive: true });
|
|
201
|
+
await fs.promises.link(srcFile, destFile);
|
|
202
|
+
}));
|
|
203
|
+
return dest;
|
|
204
|
+
}
|
|
205
|
+
async function areAllFilesInPkg(files, basePath) {
|
|
206
|
+
const allFiles = await glob('**', {
|
|
207
|
+
cwd: basePath,
|
|
208
|
+
expandDirectories: false,
|
|
209
|
+
});
|
|
210
|
+
return equals(allFiles.sort(), files.sort());
|
|
211
|
+
}
|
|
212
|
+
async function getGitTarballUrlFromLockfile(dep, opts) {
|
|
213
|
+
const { preferredVersions } = await getVersionsFromLockfile(dep, opts);
|
|
214
|
+
return preferredVersions[0]?.gitTarballUrl;
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=patchCommit.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Config } from '@pnpm/config.reader';
|
|
2
|
+
import { install } from '@pnpm/installing.commands';
|
|
3
|
+
export declare function rcOptionsTypes(): Record<string, unknown>;
|
|
4
|
+
export declare function cliOptionsTypes(): Record<string, unknown>;
|
|
5
|
+
export declare const commandNames: string[];
|
|
6
|
+
export declare function help(): string;
|
|
7
|
+
export type PatchRemoveCommandOptions = install.InstallCommandOptions & Pick<Config, 'dir' | 'lockfileDir' | 'patchesDir' | 'rootProjectManifest' | 'patchedDependencies'>;
|
|
8
|
+
export declare function handler(opts: PatchRemoveCommandOptions, params: string[]): Promise<void>;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { docsUrl } from '@pnpm/cli.utils';
|
|
4
|
+
import { types as allTypes } from '@pnpm/config.reader';
|
|
5
|
+
import { PnpmError } from '@pnpm/error';
|
|
6
|
+
import { install } from '@pnpm/installing.commands';
|
|
7
|
+
import enquirer from 'enquirer';
|
|
8
|
+
import { pick } from 'ramda';
|
|
9
|
+
import { renderHelp } from 'render-help';
|
|
10
|
+
import { updatePatchedDependencies } from './updatePatchedDependencies.js';
|
|
11
|
+
export function rcOptionsTypes() {
|
|
12
|
+
return pick([], allTypes);
|
|
13
|
+
}
|
|
14
|
+
export function cliOptionsTypes() {
|
|
15
|
+
return { ...rcOptionsTypes() };
|
|
16
|
+
}
|
|
17
|
+
export const commandNames = ['patch-remove'];
|
|
18
|
+
export function help() {
|
|
19
|
+
return renderHelp({
|
|
20
|
+
description: 'Remove existing patch files',
|
|
21
|
+
url: docsUrl('patch-remove'),
|
|
22
|
+
usages: ['pnpm patch-remove [pkg...]'],
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
export async function handler(opts, params) {
|
|
26
|
+
let patchesToRemove = params;
|
|
27
|
+
const patchedDependencies = opts.patchedDependencies ?? {};
|
|
28
|
+
if (!params.length) {
|
|
29
|
+
const allPatches = Object.keys(patchedDependencies);
|
|
30
|
+
if (allPatches.length) {
|
|
31
|
+
({ patches: patchesToRemove } = await enquirer.prompt({
|
|
32
|
+
type: 'multiselect',
|
|
33
|
+
name: 'patches',
|
|
34
|
+
message: 'Select the patch to be removed',
|
|
35
|
+
choices: allPatches,
|
|
36
|
+
validate(value) {
|
|
37
|
+
return value.length === 0 ? 'Select at least one option.' : true;
|
|
38
|
+
},
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (!patchesToRemove.length) {
|
|
43
|
+
throw new PnpmError('NO_PATCHES_TO_REMOVE', 'There are no patches that need to be removed');
|
|
44
|
+
}
|
|
45
|
+
for (const patch of patchesToRemove) {
|
|
46
|
+
if (!Object.hasOwn(patchedDependencies, patch)) {
|
|
47
|
+
throw new PnpmError('PATCH_NOT_FOUND', `Patch "${patch}" not found in patched dependencies`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const patchesDirs = new Set();
|
|
51
|
+
await Promise.all(patchesToRemove.map(async (patch) => {
|
|
52
|
+
if (Object.hasOwn(patchedDependencies, patch)) {
|
|
53
|
+
const patchFile = patchedDependencies[patch];
|
|
54
|
+
patchesDirs.add(path.dirname(patchFile));
|
|
55
|
+
await fs.rm(patchFile, { force: true });
|
|
56
|
+
delete patchedDependencies[patch];
|
|
57
|
+
}
|
|
58
|
+
}));
|
|
59
|
+
await Promise.all(Array.from(patchesDirs).map(async (dir) => {
|
|
60
|
+
try {
|
|
61
|
+
const files = await fs.readdir(dir);
|
|
62
|
+
if (!files.length) {
|
|
63
|
+
await fs.rmdir(dir);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch { }
|
|
67
|
+
}));
|
|
68
|
+
await updatePatchedDependencies(patchedDependencies, {
|
|
69
|
+
...opts,
|
|
70
|
+
workspaceDir: opts.workspaceDir ?? opts.rootProjectManifestDir,
|
|
71
|
+
});
|
|
72
|
+
return install.handler(opts);
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=patchRemove.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type EditDir = string & {
|
|
2
|
+
__brand: 'patch-edit-dir';
|
|
3
|
+
};
|
|
4
|
+
export interface EditDirState {
|
|
5
|
+
patchedPkg: string;
|
|
6
|
+
applyToAll: boolean;
|
|
7
|
+
}
|
|
8
|
+
export type State = Record<EditDir, EditDirState>;
|
|
9
|
+
export interface EditDirKeyInput {
|
|
10
|
+
editDir: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ReadEditDirStateOptions extends EditDirKeyInput {
|
|
13
|
+
modulesDir: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function readEditDirState(opts: ReadEditDirStateOptions): EditDirState | undefined;
|
|
16
|
+
export interface WriteEditDirStateOptions extends ReadEditDirStateOptions, EditDirState {
|
|
17
|
+
}
|
|
18
|
+
export declare function writeEditDirState(opts: WriteEditDirStateOptions): void;
|
package/lib/stateFile.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import util from 'node:util';
|
|
4
|
+
const createEditDirKey = (opts) => opts.editDir;
|
|
5
|
+
export function readEditDirState(opts) {
|
|
6
|
+
const state = readStateFile(opts.modulesDir);
|
|
7
|
+
if (!state)
|
|
8
|
+
return undefined;
|
|
9
|
+
const key = createEditDirKey(opts);
|
|
10
|
+
return state[key];
|
|
11
|
+
}
|
|
12
|
+
export function writeEditDirState(opts) {
|
|
13
|
+
modifyStateFile(opts.modulesDir, state => {
|
|
14
|
+
const key = createEditDirKey(opts);
|
|
15
|
+
state[key] = {
|
|
16
|
+
patchedPkg: opts.patchedPkg,
|
|
17
|
+
applyToAll: opts.applyToAll,
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function modifyStateFile(modulesDir, modifyState) {
|
|
22
|
+
const filePath = getStateFilePath(modulesDir);
|
|
23
|
+
let state = readStateFile(modulesDir);
|
|
24
|
+
if (!state) {
|
|
25
|
+
state = {};
|
|
26
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
modifyState(state);
|
|
29
|
+
fs.writeFileSync(filePath, JSON.stringify(state, undefined, 2));
|
|
30
|
+
}
|
|
31
|
+
function readStateFile(modulesDir) {
|
|
32
|
+
let fileContent;
|
|
33
|
+
try {
|
|
34
|
+
fileContent = fs.readFileSync(getStateFilePath(modulesDir), 'utf-8');
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
throw err;
|
|
41
|
+
}
|
|
42
|
+
return JSON.parse(fileContent);
|
|
43
|
+
}
|
|
44
|
+
function getStateFilePath(modulesDir) {
|
|
45
|
+
return path.join(modulesDir, '.pnpm_patches', 'state.json');
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=stateFile.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { writeSettings } from '@pnpm/config.writer';
|
|
3
|
+
import normalizePath from 'normalize-path';
|
|
4
|
+
export async function updatePatchedDependencies(patchedDependencies, opts) {
|
|
5
|
+
const workspaceDir = opts.workspaceDir ?? opts.rootProjectManifestDir;
|
|
6
|
+
for (const [patchName, patchPath] of Object.entries(patchedDependencies)) {
|
|
7
|
+
if (path.isAbsolute(patchPath)) {
|
|
8
|
+
patchedDependencies[patchName] = normalizePath(path.relative(workspaceDir, patchPath));
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
await writeSettings({
|
|
12
|
+
...opts,
|
|
13
|
+
workspaceDir,
|
|
14
|
+
updatedSettings: {
|
|
15
|
+
patchedDependencies: Object.keys(patchedDependencies).length ? patchedDependencies : undefined,
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=updatePatchedDependencies.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Config } from '@pnpm/config.reader';
|
|
2
|
+
import type { ParseWantedDependencyResult } from '@pnpm/resolving.parse-wanted-dependency';
|
|
3
|
+
import { type CreateStoreControllerOptions } from '@pnpm/store.connection-manager';
|
|
4
|
+
export type WritePackageOptions = CreateStoreControllerOptions & Pick<Config, 'registries'>;
|
|
5
|
+
export declare function writePackage(dep: ParseWantedDependencyResult, dest: string, opts: WritePackageOptions): Promise<void>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createStoreController, } from '@pnpm/store.connection-manager';
|
|
2
|
+
export async function writePackage(dep, dest, opts) {
|
|
3
|
+
const store = await createStoreController({
|
|
4
|
+
...opts,
|
|
5
|
+
packageImportMethod: 'clone-or-copy',
|
|
6
|
+
});
|
|
7
|
+
const pkgResponse = await store.ctrl.requestPackage(dep, {
|
|
8
|
+
downloadPriority: 1,
|
|
9
|
+
lockfileDir: opts.dir,
|
|
10
|
+
preferredVersions: {},
|
|
11
|
+
projectDir: opts.dir,
|
|
12
|
+
});
|
|
13
|
+
const { files } = await pkgResponse.fetching();
|
|
14
|
+
await store.ctrl.importPackage(dest, {
|
|
15
|
+
filesResponse: files,
|
|
16
|
+
force: true,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=writePackage.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pnpm/patching.commands",
|
|
3
|
+
"version": "1000.3.21",
|
|
4
|
+
"description": "Commands for creating patches",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pnpm",
|
|
7
|
+
"pnpm11",
|
|
8
|
+
"scripts"
|
|
9
|
+
],
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"funding": "https://opencollective.com/pnpm",
|
|
12
|
+
"repository": "https://github.com/pnpm/pnpm/tree/main/patching/commands",
|
|
13
|
+
"homepage": "https://github.com/pnpm/pnpm/tree/main/patching/commands#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/pnpm/pnpm/issues"
|
|
16
|
+
},
|
|
17
|
+
"type": "module",
|
|
18
|
+
"main": "lib/index.js",
|
|
19
|
+
"types": "lib/index.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": "./lib/index.js"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"lib",
|
|
25
|
+
"!*.map"
|
|
26
|
+
],
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"chalk": "^5.6.0",
|
|
29
|
+
"enquirer": "^2.4.1",
|
|
30
|
+
"escape-string-regexp": "^5.0.0",
|
|
31
|
+
"is-windows": "^1.0.2",
|
|
32
|
+
"make-empty-dir": "^4.0.0",
|
|
33
|
+
"normalize-path": "^3.0.0",
|
|
34
|
+
"ramda": "npm:@pnpm/ramda@0.28.1",
|
|
35
|
+
"realpath-missing": "^2.0.0",
|
|
36
|
+
"render-help": "^2.0.0",
|
|
37
|
+
"safe-execa": "^0.3.0",
|
|
38
|
+
"semver": "^7.7.2",
|
|
39
|
+
"terminal-link": "^4.0.0",
|
|
40
|
+
"tinyglobby": "^0.2.14",
|
|
41
|
+
"@pnpm/config.reader": "1004.4.2",
|
|
42
|
+
"@pnpm/config.writer": "1000.0.14",
|
|
43
|
+
"@pnpm/constants": "1001.3.1",
|
|
44
|
+
"@pnpm/crypto.hash": "1000.2.1",
|
|
45
|
+
"@pnpm/error": "1000.0.5",
|
|
46
|
+
"@pnpm/cli.utils": "1001.2.8",
|
|
47
|
+
"@pnpm/fetching.pick-fetcher": "1001.0.0",
|
|
48
|
+
"@pnpm/fs.packlist": "1000.0.0",
|
|
49
|
+
"@pnpm/installing.modules-yaml": "1000.3.6",
|
|
50
|
+
"@pnpm/lockfile.fs": "1001.1.21",
|
|
51
|
+
"@pnpm/installing.commands": "1004.6.10",
|
|
52
|
+
"@pnpm/lockfile.utils": "1003.0.3",
|
|
53
|
+
"@pnpm/patching.apply-patch": "1000.0.7",
|
|
54
|
+
"@pnpm/pkg-manifest.reader": "1000.1.2",
|
|
55
|
+
"@pnpm/resolving.parse-wanted-dependency": "1001.0.0",
|
|
56
|
+
"@pnpm/store.connection-manager": "1002.2.4",
|
|
57
|
+
"@pnpm/store.path": "1000.0.5",
|
|
58
|
+
"@pnpm/types": "1000.9.0",
|
|
59
|
+
"@pnpm/workspace.project-manifest-reader": "1001.1.4",
|
|
60
|
+
"@pnpm/workspace.workspace-manifest-reader": "1000.2.5"
|
|
61
|
+
},
|
|
62
|
+
"peerDependencies": {
|
|
63
|
+
"@pnpm/logger": ">=1001.0.0 <1002.0.0"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@jest/globals": "30.0.5",
|
|
67
|
+
"@pnpm/registry-mock": "5.2.4",
|
|
68
|
+
"@types/is-windows": "^1.0.2",
|
|
69
|
+
"@types/normalize-path": "^3.0.2",
|
|
70
|
+
"@types/ramda": "0.29.12",
|
|
71
|
+
"@types/semver": "7.7.1",
|
|
72
|
+
"tempy": "3.0.0",
|
|
73
|
+
"write-yaml-file": "^6.0.0",
|
|
74
|
+
"@pnpm/logger": "1001.0.1",
|
|
75
|
+
"@pnpm/patching.commands": "1000.3.21",
|
|
76
|
+
"@pnpm/prepare": "1000.0.4",
|
|
77
|
+
"@pnpm/workspace.projects-filter": "1000.0.43",
|
|
78
|
+
"@pnpm/test-fixtures": "1000.0.0"
|
|
79
|
+
},
|
|
80
|
+
"engines": {
|
|
81
|
+
"node": ">=22.13"
|
|
82
|
+
},
|
|
83
|
+
"jest": {
|
|
84
|
+
"preset": "@pnpm/jest-config/with-registry"
|
|
85
|
+
},
|
|
86
|
+
"scripts": {
|
|
87
|
+
"start": "tsgo --watch",
|
|
88
|
+
"lint": "eslint \"src/**/*.ts\" \"test/**/*.ts\"",
|
|
89
|
+
"_test": "cross-env NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules --disable-warning=ExperimentalWarning --disable-warning=DEP0169\" jest",
|
|
90
|
+
"test": "pnpm run compile && pnpm run _test",
|
|
91
|
+
"compile": "tsgo --build && pnpm run lint --fix"
|
|
92
|
+
}
|
|
93
|
+
}
|