@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.
Files changed (44) hide show
  1. package/LICENSE +22 -0
  2. package/lib/deploy/createDeployFiles.d.ts +23 -0
  3. package/lib/deploy/createDeployFiles.js +218 -0
  4. package/lib/deploy/deploy.d.ts +11 -0
  5. package/lib/deploy/deploy.js +270 -0
  6. package/lib/deploy/deployHook.d.ts +2 -0
  7. package/lib/deploy/deployHook.js +15 -0
  8. package/lib/deploy/index.d.ts +2 -0
  9. package/lib/deploy/index.js +3 -0
  10. package/lib/index.d.ts +2 -0
  11. package/lib/index.js +3 -0
  12. package/lib/publish/FailedToPublishError.d.ts +22 -0
  13. package/lib/publish/FailedToPublishError.js +40 -0
  14. package/lib/publish/displayError.d.ts +1 -0
  15. package/lib/publish/displayError.js +23 -0
  16. package/lib/publish/executeTokenHelper.d.ts +4 -0
  17. package/lib/publish/executeTokenHelper.js +14 -0
  18. package/lib/publish/extractManifestFromPacked.d.ts +12 -0
  19. package/lib/publish/extractManifestFromPacked.js +71 -0
  20. package/lib/publish/index.d.ts +3 -0
  21. package/lib/publish/index.js +4 -0
  22. package/lib/publish/oidc/authToken.d.ts +73 -0
  23. package/lib/publish/oidc/authToken.js +95 -0
  24. package/lib/publish/oidc/idToken.d.ts +76 -0
  25. package/lib/publish/oidc/idToken.js +85 -0
  26. package/lib/publish/oidc/provenance.d.ts +73 -0
  27. package/lib/publish/oidc/provenance.js +90 -0
  28. package/lib/publish/otp.d.ts +89 -0
  29. package/lib/publish/otp.js +165 -0
  30. package/lib/publish/otpEnv.d.ts +7 -0
  31. package/lib/publish/otpEnv.js +5 -0
  32. package/lib/publish/pack.d.ts +32 -0
  33. package/lib/publish/pack.js +303 -0
  34. package/lib/publish/publish.d.ts +31 -0
  35. package/lib/publish/publish.js +216 -0
  36. package/lib/publish/publishPackedPkg.d.ts +19 -0
  37. package/lib/publish/publishPackedPkg.js +233 -0
  38. package/lib/publish/recursivePublish.d.ts +12 -0
  39. package/lib/publish/recursivePublish.js +114 -0
  40. package/lib/publish/registryConfigKeys.d.ts +30 -0
  41. package/lib/publish/registryConfigKeys.js +50 -0
  42. package/lib/publish/utils/shared-context.d.ts +7 -0
  43. package/lib/publish/utils/shared-context.js +17 -0
  44. 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,2 @@
1
+ import { type BaseManifest } from '@pnpm/types';
2
+ export declare function deployHook<Pkg extends BaseManifest>(pkg: Pkg): Pkg;
@@ -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
@@ -0,0 +1,2 @@
1
+ import * as deploy from './deploy.js';
2
+ export { deploy };
@@ -0,0 +1,3 @@
1
+ import * as deploy from './deploy.js';
2
+ export { deploy };
3
+ //# sourceMappingURL=index.js.map
package/lib/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { deploy } from './deploy/index.js';
2
+ export { pack, publish } from './publish/index.js';
package/lib/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { deploy } from './deploy/index.js';
2
+ export { pack, publish } from './publish/index.js';
3
+ //# sourceMappingURL=index.js.map
@@ -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;