@pnpm/releasing.commands 1000.0.0
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/lib/deploy/createDeployFiles.d.ts +23 -0
- package/lib/deploy/createDeployFiles.js +218 -0
- package/lib/deploy/deploy.d.ts +11 -0
- package/lib/deploy/deploy.js +270 -0
- package/lib/deploy/deployHook.d.ts +2 -0
- package/lib/deploy/deployHook.js +15 -0
- package/lib/deploy/index.d.ts +2 -0
- package/lib/deploy/index.js +3 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +3 -0
- package/lib/publish/FailedToPublishError.d.ts +22 -0
- package/lib/publish/FailedToPublishError.js +40 -0
- package/lib/publish/displayError.d.ts +1 -0
- package/lib/publish/displayError.js +23 -0
- package/lib/publish/executeTokenHelper.d.ts +4 -0
- package/lib/publish/executeTokenHelper.js +14 -0
- package/lib/publish/extractManifestFromPacked.d.ts +12 -0
- package/lib/publish/extractManifestFromPacked.js +71 -0
- package/lib/publish/index.d.ts +3 -0
- package/lib/publish/index.js +4 -0
- package/lib/publish/oidc/authToken.d.ts +73 -0
- package/lib/publish/oidc/authToken.js +95 -0
- package/lib/publish/oidc/idToken.d.ts +76 -0
- package/lib/publish/oidc/idToken.js +85 -0
- package/lib/publish/oidc/provenance.d.ts +73 -0
- package/lib/publish/oidc/provenance.js +90 -0
- package/lib/publish/otp.d.ts +89 -0
- package/lib/publish/otp.js +165 -0
- package/lib/publish/otpEnv.d.ts +7 -0
- package/lib/publish/otpEnv.js +5 -0
- package/lib/publish/pack.d.ts +32 -0
- package/lib/publish/pack.js +303 -0
- package/lib/publish/publish.d.ts +31 -0
- package/lib/publish/publish.js +216 -0
- package/lib/publish/publishPackedPkg.d.ts +19 -0
- package/lib/publish/publishPackedPkg.js +233 -0
- package/lib/publish/recursivePublish.d.ts +12 -0
- package/lib/publish/recursivePublish.js +114 -0
- package/lib/publish/registryConfigKeys.d.ts +30 -0
- package/lib/publish/registryConfigKeys.js +50 -0
- package/lib/publish/utils/shared-context.d.ts +7 -0
- package/lib/publish/utils/shared-context.js +17 -0
- package/package.json +118 -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.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { LockfileObject } from '@pnpm/lockfile.types';
|
|
2
|
+
import type { PnpmSettings, Project, ProjectId, ProjectManifest } from '@pnpm/types';
|
|
3
|
+
export interface CreateDeployFilesOptions {
|
|
4
|
+
allProjects: Array<Pick<Project, 'manifest' | 'rootDirRealPath'>>;
|
|
5
|
+
deployDir: string;
|
|
6
|
+
lockfile: LockfileObject;
|
|
7
|
+
lockfileDir: string;
|
|
8
|
+
patchedDependencies?: PnpmSettings['patchedDependencies'];
|
|
9
|
+
selectedProjectManifest: ProjectManifest;
|
|
10
|
+
projectId: ProjectId;
|
|
11
|
+
rootProjectManifestDir: string;
|
|
12
|
+
allowBuilds?: Record<string, boolean | string>;
|
|
13
|
+
}
|
|
14
|
+
export interface DeployWorkspaceManifest {
|
|
15
|
+
allowBuilds?: Record<string, boolean | string>;
|
|
16
|
+
patchedDependencies?: Record<string, string>;
|
|
17
|
+
}
|
|
18
|
+
export interface DeployFiles {
|
|
19
|
+
lockfile: LockfileObject;
|
|
20
|
+
manifest: ProjectManifest;
|
|
21
|
+
workspaceManifest?: DeployWorkspaceManifest;
|
|
22
|
+
}
|
|
23
|
+
export declare function createDeployFiles({ allProjects, deployDir, lockfile, lockfileDir, patchedDependencies, selectedProjectManifest, projectId, rootProjectManifestDir, allowBuilds }: CreateDeployFilesOptions): DeployFiles;
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import url from 'node:url';
|
|
3
|
+
import * as dp from '@pnpm/deps.path';
|
|
4
|
+
import normalizePath from 'normalize-path';
|
|
5
|
+
const DEPENDENCIES_FIELD = ['dependencies', 'devDependencies', 'optionalDependencies'];
|
|
6
|
+
export function createDeployFiles({ allProjects, deployDir, lockfile, lockfileDir, patchedDependencies, selectedProjectManifest, projectId, rootProjectManifestDir, allowBuilds, }) {
|
|
7
|
+
const deployedProjectRealPath = path.resolve(lockfileDir, projectId);
|
|
8
|
+
const inputSnapshot = lockfile.importers[projectId];
|
|
9
|
+
const targetSnapshot = {
|
|
10
|
+
...inputSnapshot,
|
|
11
|
+
specifiers: {},
|
|
12
|
+
dependencies: {},
|
|
13
|
+
devDependencies: {},
|
|
14
|
+
optionalDependencies: {},
|
|
15
|
+
};
|
|
16
|
+
const targetPackageSnapshots = {};
|
|
17
|
+
for (const name in lockfile.packages) {
|
|
18
|
+
const inputDepPath = name;
|
|
19
|
+
const inputSnapshot = lockfile.packages[inputDepPath];
|
|
20
|
+
const resolveResult = resolveLinkOrFile(inputDepPath, {
|
|
21
|
+
lockfileDir,
|
|
22
|
+
projectRootDirRealPath: rootProjectManifestDir,
|
|
23
|
+
});
|
|
24
|
+
const outputDepPath = resolveResult
|
|
25
|
+
? createFileUrlDepPath(resolveResult, allProjects)
|
|
26
|
+
: inputDepPath;
|
|
27
|
+
targetPackageSnapshots[outputDepPath] = convertPackageSnapshot(inputSnapshot, {
|
|
28
|
+
allProjects,
|
|
29
|
+
deployDir,
|
|
30
|
+
deployedProjectRealPath,
|
|
31
|
+
lockfileDir,
|
|
32
|
+
projectRootDirRealPath: rootProjectManifestDir,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
for (const importerPath in lockfile.importers) {
|
|
36
|
+
if (importerPath === projectId)
|
|
37
|
+
continue;
|
|
38
|
+
const projectSnapshot = lockfile.importers[importerPath];
|
|
39
|
+
const projectRootDirRealPath = path.resolve(lockfileDir, importerPath);
|
|
40
|
+
const packageSnapshot = convertProjectSnapshotToPackageSnapshot(projectSnapshot, {
|
|
41
|
+
allProjects,
|
|
42
|
+
deployDir,
|
|
43
|
+
lockfileDir,
|
|
44
|
+
deployedProjectRealPath,
|
|
45
|
+
projectRootDirRealPath,
|
|
46
|
+
});
|
|
47
|
+
const depPath = createFileUrlDepPath({ resolvedPath: projectRootDirRealPath }, allProjects);
|
|
48
|
+
targetPackageSnapshots[depPath] = packageSnapshot;
|
|
49
|
+
}
|
|
50
|
+
for (const field of DEPENDENCIES_FIELD) {
|
|
51
|
+
const targetDependencies = targetSnapshot[field] ?? {};
|
|
52
|
+
const targetSpecifiers = targetSnapshot.specifiers;
|
|
53
|
+
const inputDependencies = inputSnapshot[field] ?? {};
|
|
54
|
+
for (const name in inputDependencies) {
|
|
55
|
+
const version = inputDependencies[name];
|
|
56
|
+
const resolveResult = resolveLinkOrFile(version, {
|
|
57
|
+
lockfileDir,
|
|
58
|
+
projectRootDirRealPath: path.resolve(lockfileDir, projectId),
|
|
59
|
+
});
|
|
60
|
+
if (!resolveResult) {
|
|
61
|
+
targetSpecifiers[name] = targetDependencies[name] = version;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
targetSpecifiers[name] = targetDependencies[name] =
|
|
65
|
+
resolveResult.resolvedPath === deployedProjectRealPath ? 'link:.' : createFileUrlDepPath(resolveResult, allProjects);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const result = {
|
|
69
|
+
lockfile: {
|
|
70
|
+
...lockfile,
|
|
71
|
+
patchedDependencies: undefined,
|
|
72
|
+
overrides: undefined, // the effects of the overrides should already be part of the package snapshots
|
|
73
|
+
packageExtensionsChecksum: undefined, // the effects of the package extensions should already be part of the package snapshots
|
|
74
|
+
pnpmfileChecksum: undefined, // the effects of the pnpmfile should already be part of the package snapshots
|
|
75
|
+
settings: {
|
|
76
|
+
...lockfile.settings,
|
|
77
|
+
injectWorkspacePackages: undefined, // the effects of injecting workspace packages should already be part of the lockfile
|
|
78
|
+
},
|
|
79
|
+
importers: {
|
|
80
|
+
['.']: targetSnapshot,
|
|
81
|
+
},
|
|
82
|
+
packages: targetPackageSnapshots,
|
|
83
|
+
},
|
|
84
|
+
manifest: {
|
|
85
|
+
...selectedProjectManifest,
|
|
86
|
+
dependencies: targetSnapshot.dependencies,
|
|
87
|
+
devDependencies: targetSnapshot.devDependencies,
|
|
88
|
+
optionalDependencies: targetSnapshot.optionalDependencies,
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
if (lockfile.patchedDependencies && patchedDependencies) {
|
|
92
|
+
result.lockfile.patchedDependencies = { ...lockfile.patchedDependencies };
|
|
93
|
+
const deployManifestPatchedDeps = {};
|
|
94
|
+
for (const name in patchedDependencies) {
|
|
95
|
+
const absolutePath = patchedDependencies[name];
|
|
96
|
+
const relativePath = normalizePath(path.relative(deployDir, absolutePath));
|
|
97
|
+
deployManifestPatchedDeps[name] = relativePath;
|
|
98
|
+
}
|
|
99
|
+
result.workspaceManifest = {
|
|
100
|
+
...result.workspaceManifest,
|
|
101
|
+
patchedDependencies: deployManifestPatchedDeps,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
if (allowBuilds) {
|
|
105
|
+
result.workspaceManifest = {
|
|
106
|
+
...result.workspaceManifest,
|
|
107
|
+
allowBuilds,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
function convertPackageSnapshot(inputSnapshot, opts) {
|
|
113
|
+
const inputResolution = inputSnapshot.resolution;
|
|
114
|
+
let outputResolution;
|
|
115
|
+
if ('integrity' in inputResolution) {
|
|
116
|
+
outputResolution = inputResolution;
|
|
117
|
+
}
|
|
118
|
+
else if ('tarball' in inputResolution && typeof inputResolution.tarball === 'string') {
|
|
119
|
+
outputResolution = { ...inputResolution };
|
|
120
|
+
if (inputResolution.tarball.startsWith('file:')) {
|
|
121
|
+
const inputPath = inputResolution.tarball.slice('file:'.length);
|
|
122
|
+
const resolvedPath = path.resolve(opts.lockfileDir, inputPath);
|
|
123
|
+
const outputPath = normalizePath(path.relative(opts.deployDir, resolvedPath));
|
|
124
|
+
outputResolution.tarball = `file:${outputPath}`;
|
|
125
|
+
if ('path' in inputResolution && typeof inputResolution.path === 'string') {
|
|
126
|
+
outputResolution.path = outputPath;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else if (inputResolution.type === 'directory') {
|
|
131
|
+
const dirResolution = inputResolution;
|
|
132
|
+
const resolvedPath = path.resolve(opts.lockfileDir, dirResolution.directory);
|
|
133
|
+
const directory = normalizePath(path.relative(opts.deployDir, resolvedPath));
|
|
134
|
+
outputResolution = { ...dirResolution, directory };
|
|
135
|
+
}
|
|
136
|
+
else if (inputResolution.type === 'git' || inputResolution.type === 'variations') {
|
|
137
|
+
outputResolution = inputResolution;
|
|
138
|
+
}
|
|
139
|
+
else if (inputResolution.type && typeof inputResolution.type === 'string') {
|
|
140
|
+
// Custom resolution type - pass through as-is
|
|
141
|
+
outputResolution = inputResolution;
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
throw new Error(`Unknown resolution type: ${JSON.stringify(inputResolution)}`);
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
...inputSnapshot,
|
|
148
|
+
resolution: outputResolution,
|
|
149
|
+
dependencies: convertResolvedDependencies(inputSnapshot.dependencies, opts),
|
|
150
|
+
optionalDependencies: convertResolvedDependencies(inputSnapshot.optionalDependencies, opts),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
function convertProjectSnapshotToPackageSnapshot(projectSnapshot, opts) {
|
|
154
|
+
const resolution = {
|
|
155
|
+
type: 'directory',
|
|
156
|
+
directory: normalizePath(path.relative(opts.deployDir, opts.projectRootDirRealPath)),
|
|
157
|
+
};
|
|
158
|
+
const dependencies = convertResolvedDependencies(projectSnapshot.dependencies, opts);
|
|
159
|
+
const optionalDependencies = convertResolvedDependencies(projectSnapshot.optionalDependencies, opts);
|
|
160
|
+
return {
|
|
161
|
+
dependencies,
|
|
162
|
+
optionalDependencies,
|
|
163
|
+
resolution,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function convertResolvedDependencies(input, opts) {
|
|
167
|
+
if (!input)
|
|
168
|
+
return undefined;
|
|
169
|
+
const output = {};
|
|
170
|
+
for (const key in input) {
|
|
171
|
+
const version = input[key];
|
|
172
|
+
const resolveResult = resolveLinkOrFile(version, opts);
|
|
173
|
+
if (!resolveResult) {
|
|
174
|
+
output[key] = version;
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (resolveResult.resolvedPath === opts.deployedProjectRealPath) {
|
|
178
|
+
output[key] = 'link:.'; // the path is relative to the lockfile dir, which means '.' would reference the deploy dir
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
output[key] = createFileUrlDepPath(resolveResult, opts.allProjects);
|
|
182
|
+
}
|
|
183
|
+
return output;
|
|
184
|
+
}
|
|
185
|
+
function resolveLinkOrFile(pkgVer, opts) {
|
|
186
|
+
const { lockfileDir, projectRootDirRealPath } = opts;
|
|
187
|
+
function resolveScheme(scheme, base) {
|
|
188
|
+
if (!pkgVer.startsWith(scheme))
|
|
189
|
+
return undefined;
|
|
190
|
+
const { id, peerDepGraphHash: suffix } = dp.parseDepPath(pkgVer.slice(scheme.length));
|
|
191
|
+
const resolvedPath = path.resolve(base, id);
|
|
192
|
+
return { scheme, resolvedPath, suffix };
|
|
193
|
+
}
|
|
194
|
+
const resolveSchemeResult = resolveScheme('file:', lockfileDir) ?? resolveScheme('link:', projectRootDirRealPath);
|
|
195
|
+
if (resolveSchemeResult)
|
|
196
|
+
return resolveSchemeResult;
|
|
197
|
+
const { nonSemverVersion, patchHash, peerDepGraphHash, version } = dp.parse(pkgVer);
|
|
198
|
+
if (!nonSemverVersion)
|
|
199
|
+
return undefined;
|
|
200
|
+
if (version) {
|
|
201
|
+
throw new Error(`Something goes wrong, version should be undefined but isn't: ${version}`);
|
|
202
|
+
}
|
|
203
|
+
const parseResult = resolveLinkOrFile(nonSemverVersion, opts);
|
|
204
|
+
if (!parseResult)
|
|
205
|
+
return undefined;
|
|
206
|
+
if (parseResult.suffix) {
|
|
207
|
+
throw new Error(`Something goes wrong, suffix should be undefined but isn't: ${parseResult.suffix}`);
|
|
208
|
+
}
|
|
209
|
+
parseResult.suffix = `${patchHash ?? ''}${peerDepGraphHash ?? ''}`;
|
|
210
|
+
return parseResult;
|
|
211
|
+
}
|
|
212
|
+
function createFileUrlDepPath({ resolvedPath, suffix }, allProjects) {
|
|
213
|
+
const depFileUrl = url.pathToFileURL(resolvedPath).toString();
|
|
214
|
+
const project = allProjects.find(project => project.rootDirRealPath === resolvedPath);
|
|
215
|
+
const name = project?.manifest.name ?? path.basename(resolvedPath);
|
|
216
|
+
return `${name}@${depFileUrl}${suffix ?? ''}`;
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=createDeployFiles.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Config } from '@pnpm/config.reader';
|
|
2
|
+
import { install } from '@pnpm/installing.commands';
|
|
3
|
+
export declare const shorthands: {
|
|
4
|
+
legacy: string[];
|
|
5
|
+
};
|
|
6
|
+
export declare function rcOptionsTypes(): Record<string, unknown>;
|
|
7
|
+
export declare function cliOptionsTypes(): Record<string, unknown>;
|
|
8
|
+
export declare const commandNames: string[];
|
|
9
|
+
export declare function help(): string;
|
|
10
|
+
export type DeployOptions = Omit<install.InstallCommandOptions, 'useLockfile'> & Pick<Config, 'allowBuilds' | 'forceLegacyDeploy'>;
|
|
11
|
+
export declare function handler(opts: DeployOptions, params: string[]): Promise<void>;
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { FILTERING } from '@pnpm/cli.common-cli-options-help';
|
|
4
|
+
import { docsUrl } from '@pnpm/cli.utils';
|
|
5
|
+
import { types as configTypes } from '@pnpm/config.reader';
|
|
6
|
+
import { WORKSPACE_MANIFEST_FILENAME } from '@pnpm/constants';
|
|
7
|
+
import { PnpmError } from '@pnpm/error';
|
|
8
|
+
import { fetchFromDir } from '@pnpm/fetching.directory-fetcher';
|
|
9
|
+
import { createIndexedPkgImporter } from '@pnpm/fs.indexed-pkg-importer';
|
|
10
|
+
import { isEmptyDirOrNothing } from '@pnpm/fs.is-empty-dir-or-nothing';
|
|
11
|
+
import { install } from '@pnpm/installing.commands';
|
|
12
|
+
import { getLockfileImporterId, readWantedLockfile, writeWantedLockfile } from '@pnpm/lockfile.fs';
|
|
13
|
+
import { globalWarn, logger } from '@pnpm/logger';
|
|
14
|
+
import { rimraf } from '@zkochan/rimraf';
|
|
15
|
+
import { pick } from 'ramda';
|
|
16
|
+
import { renderHelp } from 'render-help';
|
|
17
|
+
import { writeYamlFile } from 'write-yaml-file';
|
|
18
|
+
import { createDeployFiles } from './createDeployFiles.js';
|
|
19
|
+
import { deployHook } from './deployHook.js';
|
|
20
|
+
const FORCE_LEGACY_DEPLOY = 'force-legacy-deploy';
|
|
21
|
+
export const shorthands = {
|
|
22
|
+
...install.shorthands,
|
|
23
|
+
legacy: [`--config.${FORCE_LEGACY_DEPLOY}=true`],
|
|
24
|
+
};
|
|
25
|
+
const DEPLOY_OWN_OPTIONS = pick([FORCE_LEGACY_DEPLOY], configTypes);
|
|
26
|
+
export function rcOptionsTypes() {
|
|
27
|
+
return {
|
|
28
|
+
...install.rcOptionsTypes(),
|
|
29
|
+
...DEPLOY_OWN_OPTIONS,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function cliOptionsTypes() {
|
|
33
|
+
return {
|
|
34
|
+
...install.cliOptionsTypes(),
|
|
35
|
+
...DEPLOY_OWN_OPTIONS,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export const commandNames = ['deploy'];
|
|
39
|
+
export function help() {
|
|
40
|
+
return renderHelp({
|
|
41
|
+
description: 'Experimental! Deploy a package from a workspace',
|
|
42
|
+
url: docsUrl('deploy'),
|
|
43
|
+
usages: ['pnpm --filter=<deployed project name> deploy <target directory>'],
|
|
44
|
+
descriptionLists: [
|
|
45
|
+
{
|
|
46
|
+
title: 'Options',
|
|
47
|
+
list: [
|
|
48
|
+
{
|
|
49
|
+
description: "Packages in `devDependencies` won't be installed",
|
|
50
|
+
name: '--prod',
|
|
51
|
+
shortAlias: '-P',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
description: 'Only `devDependencies` are installed',
|
|
55
|
+
name: '--dev',
|
|
56
|
+
shortAlias: '-D',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
description: '`optionalDependencies` are not installed',
|
|
60
|
+
name: '--no-optional',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
description: 'Force legacy deploy implementation',
|
|
64
|
+
name: '--legacy',
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
FILTERING,
|
|
69
|
+
],
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
export async function handler(opts, params) {
|
|
73
|
+
if (!opts.workspaceDir) {
|
|
74
|
+
let hint;
|
|
75
|
+
if (opts.rootProjectManifest?.scripts?.['deploy'] != null) {
|
|
76
|
+
hint = 'Maybe you wanted to invoke "pnpm run deploy"';
|
|
77
|
+
}
|
|
78
|
+
throw new PnpmError('CANNOT_DEPLOY', 'A deploy is only possible from inside a workspace', { hint });
|
|
79
|
+
}
|
|
80
|
+
const selectedProjects = Object.values(opts.selectedProjectsGraph ?? {});
|
|
81
|
+
if (selectedProjects.length === 0) {
|
|
82
|
+
throw new PnpmError('NOTHING_TO_DEPLOY', 'No project was selected for deployment');
|
|
83
|
+
}
|
|
84
|
+
if (selectedProjects.length > 1) {
|
|
85
|
+
throw new PnpmError('CANNOT_DEPLOY_MANY', 'Cannot deploy more than 1 project');
|
|
86
|
+
}
|
|
87
|
+
if (params.length !== 1) {
|
|
88
|
+
throw new PnpmError('INVALID_DEPLOY_TARGET', 'This command requires one parameter');
|
|
89
|
+
}
|
|
90
|
+
const selectedProject = selectedProjects[0].package;
|
|
91
|
+
const deployDirParam = params[0];
|
|
92
|
+
const deployDir = path.isAbsolute(deployDirParam) ? deployDirParam : path.join(opts.dir, deployDirParam);
|
|
93
|
+
if (!isEmptyDirOrNothing(deployDir)) {
|
|
94
|
+
if (!opts.force) {
|
|
95
|
+
throw new PnpmError('DEPLOY_DIR_NOT_EMPTY', `Deploy path ${deployDir} is not empty`);
|
|
96
|
+
}
|
|
97
|
+
logger.warn({ message: 'using --force, deleting deploy path', prefix: deployDir });
|
|
98
|
+
}
|
|
99
|
+
await rimraf(deployDir);
|
|
100
|
+
await fs.promises.mkdir(deployDir, { recursive: true });
|
|
101
|
+
const includeOnlyPackageFiles = !opts.deployAllFiles;
|
|
102
|
+
await copyProject(selectedProject.rootDir, deployDir, { includeOnlyPackageFiles });
|
|
103
|
+
if (opts.sharedWorkspaceLockfile) {
|
|
104
|
+
const warning = opts.forceLegacyDeploy
|
|
105
|
+
? 'Shared workspace lockfile detected but configuration forces legacy deploy implementation.'
|
|
106
|
+
: await deployFromSharedLockfile(opts, selectedProject, deployDir);
|
|
107
|
+
if (warning) {
|
|
108
|
+
globalWarn(warning);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const deployedProject = opts.allProjects?.find(({ rootDir }) => rootDir === selectedProject.rootDir);
|
|
115
|
+
if (deployedProject) {
|
|
116
|
+
deployedProject.modulesDir = path.relative(selectedProject.rootDir, path.join(deployDir, 'node_modules'));
|
|
117
|
+
}
|
|
118
|
+
await install.handler({
|
|
119
|
+
...opts,
|
|
120
|
+
confirmModulesPurge: false,
|
|
121
|
+
// Deploy doesn't work with dedupePeerDependents=true currently as for deploy
|
|
122
|
+
// we need to select a single project for install, while dedupePeerDependents
|
|
123
|
+
// doesn't work with filters right now.
|
|
124
|
+
// Related issue: https://github.com/pnpm/pnpm/issues/6858
|
|
125
|
+
dedupePeerDependents: false,
|
|
126
|
+
// If enabled, dedupe-injected-deps will symlink workspace packages in the
|
|
127
|
+
// deployed dir to their original (non-deployed) directory in an attempt to
|
|
128
|
+
// dedupe workspace packages that don't need to be injected. The deployed
|
|
129
|
+
// dir shouldn't have symlinks to the original workspace. Disable
|
|
130
|
+
// dedupe-injected-deps to always inject workspace packages since copying is
|
|
131
|
+
// desirable.
|
|
132
|
+
dedupeInjectedDeps: false,
|
|
133
|
+
// Compute the wanted lockfile correctly by setting pruneLockfileImporters.
|
|
134
|
+
// Since pnpm deploy only installs dependencies for a single selected
|
|
135
|
+
// project, other projects in the "importers" lockfile section will be
|
|
136
|
+
// empty when node-linker=hoisted.
|
|
137
|
+
//
|
|
138
|
+
// For example, when deploying project-1, project-2 may not be populated,
|
|
139
|
+
// even if it has dependencies.
|
|
140
|
+
//
|
|
141
|
+
// importers:
|
|
142
|
+
// project-1:
|
|
143
|
+
// dependencies:
|
|
144
|
+
// foo:
|
|
145
|
+
// specifier: ^1.0.0
|
|
146
|
+
// version: ^1.0.0
|
|
147
|
+
// project-2: {}
|
|
148
|
+
//
|
|
149
|
+
// Avoid including these empty importers in the in-memory wanted lockfile.
|
|
150
|
+
// This is important when node-linker=hoisted to prevent project-2 from
|
|
151
|
+
// being included in the hoisted install. If project-2 is errantly hoisted
|
|
152
|
+
// to the root node_modules dir, downstream logic will fail to inject it to
|
|
153
|
+
// the deploy directory. It's also just weird to include empty importers
|
|
154
|
+
// that don't matter to the filtered lockfile generated for pnpm deploy.
|
|
155
|
+
pruneLockfileImporters: true,
|
|
156
|
+
// The node_modules for a pnpm deploy should be self-contained. The global
|
|
157
|
+
// virtual store would create symlinks outside of the deploy directory.
|
|
158
|
+
enableGlobalVirtualStore: false,
|
|
159
|
+
depth: Infinity,
|
|
160
|
+
hooks: {
|
|
161
|
+
...opts.hooks,
|
|
162
|
+
readPackage: [
|
|
163
|
+
...(opts.hooks?.readPackage ?? []),
|
|
164
|
+
deployHook,
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
frozenLockfile: false,
|
|
168
|
+
preferFrozenLockfile: false,
|
|
169
|
+
// Deploy doesn't work currently with hoisted node_modules.
|
|
170
|
+
// TODO: make it work as we need to prefer packages from the lockfile during deployment.
|
|
171
|
+
useLockfile: opts.nodeLinker !== 'hoisted',
|
|
172
|
+
saveLockfile: false,
|
|
173
|
+
virtualStoreDir: path.join(deployDir, 'node_modules/.pnpm'),
|
|
174
|
+
modulesDir: path.relative(opts.workspaceDir, path.join(deployDir, 'node_modules')),
|
|
175
|
+
rawLocalConfig: {
|
|
176
|
+
...opts.rawLocalConfig,
|
|
177
|
+
// This is a workaround to prevent frozen install in CI envs.
|
|
178
|
+
'frozen-lockfile': false,
|
|
179
|
+
},
|
|
180
|
+
includeOnlyPackageFiles,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
async function copyProject(src, dest, opts) {
|
|
184
|
+
const { filesMap } = await fetchFromDir(src, opts);
|
|
185
|
+
const importPkg = createIndexedPkgImporter('clone-or-copy');
|
|
186
|
+
importPkg(dest, { filesMap, force: true, resolvedFrom: 'local-dir' });
|
|
187
|
+
}
|
|
188
|
+
async function deployFromSharedLockfile(opts, selectedProject, deployDir) {
|
|
189
|
+
if (!opts.injectWorkspacePackages) {
|
|
190
|
+
throw new PnpmError('DEPLOY_NONINJECTED_WORKSPACE', 'By default, starting from pnpm v10, we only deploy from workspaces that have "inject-workspace-packages=true" set', {
|
|
191
|
+
hint: 'If you want to deploy without using injected dependencies, run "pnpm deploy" with the "--legacy" flag or set "force-legacy-deploy" to true',
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
const { allProjects, lockfileDir, rootProjectManifestDir, workspaceDir, } = opts;
|
|
195
|
+
// The following errors should not be possible. It is a programmer error if they are reached.
|
|
196
|
+
if (!allProjects)
|
|
197
|
+
throw new Error('opts.allProjects is undefined.');
|
|
198
|
+
if (!lockfileDir)
|
|
199
|
+
throw new Error('opts.lockfileDir is undefined.');
|
|
200
|
+
if (!workspaceDir)
|
|
201
|
+
throw new Error('opts.workspaceDir is undefined.');
|
|
202
|
+
const lockfile = await readWantedLockfile(lockfileDir, { ignoreIncompatible: false });
|
|
203
|
+
if (!lockfile) {
|
|
204
|
+
return 'Shared lockfile not found. Falling back to installing without a lockfile.';
|
|
205
|
+
}
|
|
206
|
+
const projectId = getLockfileImporterId(lockfileDir, selectedProject.rootDir);
|
|
207
|
+
const deployFiles = createDeployFiles({
|
|
208
|
+
allProjects,
|
|
209
|
+
deployDir,
|
|
210
|
+
lockfile,
|
|
211
|
+
lockfileDir,
|
|
212
|
+
patchedDependencies: opts.patchedDependencies,
|
|
213
|
+
selectedProjectManifest: selectedProject.manifest,
|
|
214
|
+
projectId,
|
|
215
|
+
rootProjectManifestDir,
|
|
216
|
+
allowBuilds: opts.allowBuilds,
|
|
217
|
+
});
|
|
218
|
+
const filesToWrite = [
|
|
219
|
+
fs.promises.writeFile(path.join(deployDir, 'package.json'), JSON.stringify(deployFiles.manifest, undefined, 2) + '\n'),
|
|
220
|
+
writeWantedLockfile(deployDir, deployFiles.lockfile),
|
|
221
|
+
];
|
|
222
|
+
if (deployFiles.workspaceManifest) {
|
|
223
|
+
filesToWrite.push(writeYamlFile(path.join(deployDir, WORKSPACE_MANIFEST_FILENAME), deployFiles.workspaceManifest));
|
|
224
|
+
}
|
|
225
|
+
await Promise.all(filesToWrite);
|
|
226
|
+
try {
|
|
227
|
+
await install.handler({
|
|
228
|
+
...opts,
|
|
229
|
+
allProjects: undefined,
|
|
230
|
+
allProjectsGraph: undefined,
|
|
231
|
+
selectedProjectsGraph: undefined,
|
|
232
|
+
rootProjectManifest: deployFiles.manifest,
|
|
233
|
+
// The node_modules for a pnpm deploy should be self-contained. The global
|
|
234
|
+
// virtual store would create symlinks outside of the deploy directory.
|
|
235
|
+
enableGlobalVirtualStore: false,
|
|
236
|
+
rootProjectManifestDir: deployDir,
|
|
237
|
+
dir: deployDir,
|
|
238
|
+
lockfileDir: deployDir,
|
|
239
|
+
workspaceDir: undefined,
|
|
240
|
+
virtualStoreDir: undefined,
|
|
241
|
+
modulesDir: undefined,
|
|
242
|
+
confirmModulesPurge: false,
|
|
243
|
+
frozenLockfile: true,
|
|
244
|
+
injectWorkspacePackages: undefined, // the effects of injecting workspace packages should already be part of the package snapshots
|
|
245
|
+
overrides: undefined, // the effects of the overrides should already be part of the package snapshots
|
|
246
|
+
packageExtensions: undefined, // the effects of the package extensions should already be part of the package snapshots
|
|
247
|
+
hooks: {
|
|
248
|
+
...opts.hooks,
|
|
249
|
+
readPackage: [
|
|
250
|
+
...(opts.hooks?.readPackage ?? []),
|
|
251
|
+
deployHook,
|
|
252
|
+
],
|
|
253
|
+
calculatePnpmfileChecksum: undefined, // the effects of the pnpmfile should already be part of the package snapshots
|
|
254
|
+
},
|
|
255
|
+
rawLocalConfig: {
|
|
256
|
+
...opts.rawLocalConfig,
|
|
257
|
+
'frozen-lockfile': true,
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
globalWarn(`Deployment with a shared lockfile has failed. If this is a bug, please report it at <https://github.com/pnpm/pnpm/issues>.
|
|
263
|
+
As a workaround, add the following to pnpm-workspace.yaml:
|
|
264
|
+
|
|
265
|
+
forceLegacyDeploy: true`);
|
|
266
|
+
throw error;
|
|
267
|
+
}
|
|
268
|
+
return undefined;
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=deploy.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { DEPENDENCIES_FIELDS } from '@pnpm/types';
|
|
2
|
+
export function deployHook(pkg) {
|
|
3
|
+
pkg.dependenciesMeta = pkg.dependenciesMeta ?? {};
|
|
4
|
+
for (const depField of DEPENDENCIES_FIELDS) {
|
|
5
|
+
for (const [depName, depVersion] of Object.entries(pkg[depField] ?? {})) {
|
|
6
|
+
if (depVersion.startsWith('workspace:')) {
|
|
7
|
+
pkg.dependenciesMeta[depName] = {
|
|
8
|
+
injected: true,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return pkg;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=deployHook.js.map
|
package/lib/index.d.ts
ADDED
package/lib/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { PnpmError } from '@pnpm/error';
|
|
2
|
+
import type { PackResult } from './pack.js';
|
|
3
|
+
interface PublishErrorProperties<Pack> {
|
|
4
|
+
readonly pack: Pack;
|
|
5
|
+
readonly status: number;
|
|
6
|
+
readonly statusText: string;
|
|
7
|
+
readonly text: string;
|
|
8
|
+
}
|
|
9
|
+
export declare class FailedToPublishError<Pack extends Pick<PackResult, 'publishedManifest'>> extends PnpmError implements PublishErrorProperties<Pack> {
|
|
10
|
+
readonly pack: Pack;
|
|
11
|
+
readonly status: number;
|
|
12
|
+
readonly statusText: string;
|
|
13
|
+
readonly text: string;
|
|
14
|
+
constructor(opts: PublishErrorProperties<Pack>);
|
|
15
|
+
}
|
|
16
|
+
export declare function createFailedToPublishError<Pack extends Pick<PackResult, 'publishedManifest'>>(pack: Pack, fetchResponse: FetchResponse): Promise<FailedToPublishError<Pack>>;
|
|
17
|
+
interface FetchResponse {
|
|
18
|
+
readonly status: number;
|
|
19
|
+
readonly statusText: string;
|
|
20
|
+
readonly text: (this: FetchResponse) => string | Promise<string>;
|
|
21
|
+
}
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { PnpmError } from '@pnpm/error';
|
|
2
|
+
export class FailedToPublishError extends PnpmError {
|
|
3
|
+
pack;
|
|
4
|
+
status;
|
|
5
|
+
statusText;
|
|
6
|
+
text;
|
|
7
|
+
constructor(opts) {
|
|
8
|
+
const { pack, status, statusText, text } = opts;
|
|
9
|
+
const { name, version } = pack.publishedManifest;
|
|
10
|
+
const statusDisplay = statusText ? `${status} ${statusText}` : status;
|
|
11
|
+
const trimmedText = text.trim();
|
|
12
|
+
let message = `Failed to publish package ${name}@${version} (status ${statusDisplay})`;
|
|
13
|
+
if (trimmedText.includes('\n')) {
|
|
14
|
+
message += '\nDetails:\n';
|
|
15
|
+
for (const line of text.trimEnd().split('\n')) {
|
|
16
|
+
message += ` ${line}\n`;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
else if (trimmedText) {
|
|
20
|
+
message += `: ${trimmedText}`;
|
|
21
|
+
}
|
|
22
|
+
super('FAILED_TO_PUBLISH', message);
|
|
23
|
+
this.pack = pack;
|
|
24
|
+
this.status = status;
|
|
25
|
+
this.statusText = statusText;
|
|
26
|
+
this.text = text;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export async function createFailedToPublishError(pack, fetchResponse) {
|
|
30
|
+
const { status, statusText } = fetchResponse;
|
|
31
|
+
let text;
|
|
32
|
+
try {
|
|
33
|
+
text = await fetchResponse.text();
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
text = '';
|
|
37
|
+
}
|
|
38
|
+
return new FailedToPublishError({ pack, status, statusText, text });
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=FailedToPublishError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function displayError(error: unknown): string;
|