@pnpm/releasing.commands 1100.5.5 → 1100.6.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/CHANGELOG.md CHANGED
@@ -1,5 +1,55 @@
1
1
  # @pnpm/releasing.commands
2
2
 
3
+ ## 1100.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Added native workspace release management [#12952](https://github.com/pnpm/pnpm/issues/12952): the new `pnpm change` command records change intents as changesets-compatible `.changeset/*.md` files (`pnpm change status` shows the pending release plan), and the bare `pnpm version -r` consumes them — bumping versions across the workspace with dependent propagation through `workspace:` ranges, fixed groups, a `maxBump` cap, `--filter` narrowing, and `--dry-run` — writing changelogs, and recording consumed intents in a committed ledger that keeps cherry-picks and merge-backs between release branches safe. Packages can be moved onto per-package release lanes with the new `pnpm lane <name> --filter <pkg>` command and back with `pnpm lane main --filter <pkg>` (`pnpm lane` shows the membership), releasing `X.Y.Z-lane.N` prereleases from the same runs that release stable versions of the packages on the main lane. Configuration lives under the new `versioning` key of `pnpm-workspace.yaml` (`fixed`, `ignore`, `maxBump`, `lanes`, `changelog`). When two workspace projects publish the same name, intent files, `versioning.lanes`, and `versioning.fixed`/`ignore` may reference a project by its workspace-relative directory path (e.g. `"./pnpm/npm/pnpm"`) — the one additive extension to the changesets format, applied automatically by `pnpm change`.
8
+
9
+ Release changelogs default to `registry` storage (`versioning.changelog.storage`): no `CHANGELOG.md` is committed. Each release's section is composed at publish time and packed into the published tarball on top of the previously published version's changelog, and the consumed change intents are garbage-collected by a later `pnpm version -r` only once the registry confirms the version is published with its section. Set `versioning.changelog.storage: repository` to keep committed `CHANGELOG.md` files instead.
10
+
11
+ ### Patch Changes
12
+
13
+ - `pnpm deploy` now supports workspaces that use catalogs.
14
+
15
+ - Fixed `pnpm deploy` with a shared lockfile so local `file:` tarball dependencies keep their package name in the generated deploy lockfile. This prevents warm-store deploys from failing with `ERR_PNPM_UNEXPECTED_PKG_CONTENT_IN_STORE` when the tarball filename includes the version.
16
+
17
+ - Fixed `pnpm publish --otp` and `pnpm publish --batch --otp` to send the configured OTP to the registry.
18
+
19
+ - `pnpm publish` again sends the package's README to the registry as metadata, so registries can render it on the package page. The readme is always included in the published metadata (matching the npm CLI), while the `embed-readme` setting continues to control only whether the readme is written into the `package.json` inside the tarball. This restores the behavior that was lost when publishing became fully native. Closes pnpm/pnpm#12966.
20
+
21
+ - Limit modern deploy lockfiles and localized virtual stores to dependencies reachable from the selected dependency groups.
22
+
23
+ - `pnpm pack` now respects workspace-root `.npmignore` and `.gitignore` files when packing workspace packages.
24
+
25
+ - Updated dependencies:
26
+ - @pnpm/bins.resolver@1100.0.9
27
+ - @pnpm/cli.utils@1101.0.14
28
+ - @pnpm/config.pick-registry-for-package@1100.0.10
29
+ - @pnpm/config.reader@1101.12.0
30
+ - @pnpm/deps.path@1100.0.9
31
+ - @pnpm/engine.runtime.commands@1100.1.11
32
+ - @pnpm/engine.runtime.node-resolver@1101.1.13
33
+ - @pnpm/exec.lifecycle@1100.1.3
34
+ - @pnpm/fetching.directory-fetcher@1100.0.20
35
+ - @pnpm/fs.indexed-pkg-importer@1100.0.18
36
+ - @pnpm/fs.packlist@1100.0.2
37
+ - @pnpm/installing.client@1100.2.14
38
+ - @pnpm/installing.commands@1100.10.5
39
+ - @pnpm/lockfile.fs@1100.1.11
40
+ - @pnpm/lockfile.types@1100.0.14
41
+ - @pnpm/network.auth-header@1101.1.4
42
+ - @pnpm/network.fetch@1100.1.5
43
+ - @pnpm/releasing.exportable-manifest@1100.1.10
44
+ - @pnpm/releasing.versioning@1100.1.0
45
+ - @pnpm/resolving.npm-resolver@1102.1.3
46
+ - @pnpm/resolving.registry.types@1100.1.4
47
+ - @pnpm/resolving.resolver-base@1100.5.2
48
+ - @pnpm/types@1101.4.0
49
+ - @pnpm/workspace.projects-filter@1100.0.27
50
+ - @pnpm/workspace.projects-sorter@1100.0.9
51
+ - @pnpm/workspace.workspace-manifest-writer@1100.0.16
52
+
3
53
  ## 1100.5.5
4
54
 
5
55
  ### Patch Changes
@@ -0,0 +1,41 @@
1
+ import type { Config } from '@pnpm/config.reader';
2
+ import { type ChangeIntent, type ReleasePlan, type WorkspaceProject } from '@pnpm/releasing.versioning';
3
+ import type { Project, VersioningSettings } from '@pnpm/types';
4
+ export declare function rcOptionsTypes(): Record<string, unknown>;
5
+ export declare function cliOptionsTypes(): Record<string, unknown>;
6
+ export declare const commandNames: string[];
7
+ export declare function help(): string;
8
+ export type ChangeCommandOptions = Pick<Config, 'changedFilesIgnorePattern' | 'dir' | 'testPattern' | 'versioning' | 'workspaceDir'> & {
9
+ allProjects?: Project[];
10
+ bump?: string;
11
+ summary?: string;
12
+ };
13
+ export declare function handler(opts: ChangeCommandOptions, params: string[]): Promise<string>;
14
+ export declare function renderReleasePlan(plan: ReleasePlan): string;
15
+ export interface ReleasableProject {
16
+ name: string;
17
+ /** Workspace-relative project directory. */
18
+ dir: string;
19
+ /**
20
+ * How an intent file or versioning config should reference this project:
21
+ * the bare name, or the `./`-prefixed directory when the name is shared by
22
+ * several workspace projects.
23
+ */
24
+ ref: string;
25
+ }
26
+ /**
27
+ * The projects a change intent may demand a release from: named, carrying a
28
+ * valid semver version, and not frozen by `versioning.ignore`. Matches the
29
+ * participant set of the release-plan assembler.
30
+ */
31
+ export declare function getReleasableProjects(allProjects: Array<Pick<Project, 'manifest' | 'rootDir'>>, workspaceDir: string, versioning?: VersioningSettings): ReleasableProject[];
32
+ export declare function toWorkspaceProjects(allProjects: Array<Pick<Project, 'manifest' | 'rootDir'>>): WorkspaceProject[];
33
+ export type { ChangeIntent };
34
+ export declare const change: {
35
+ handler: typeof handler;
36
+ help: typeof help;
37
+ commandNames: string[];
38
+ cliOptionsTypes: typeof cliOptionsTypes;
39
+ rcOptionsTypes: typeof rcOptionsTypes;
40
+ recursiveByDefault: boolean;
41
+ };
@@ -0,0 +1,245 @@
1
+ import { checkbox, input, Separator } from '@inquirer/prompts';
2
+ import { PnpmError } from '@pnpm/error';
3
+ import { assembleReleasePlan, BUMP_TYPES, indexProjectRefs, readChangeIntents, readLedger, toProjectDir, writeChangeIntent, } from '@pnpm/releasing.versioning';
4
+ import { getChangedProjects } from '@pnpm/workspace.projects-filter';
5
+ import { safeExeca as execa } from 'execa';
6
+ import { renderHelp } from 'render-help';
7
+ import { valid } from 'semver';
8
+ export function rcOptionsTypes() {
9
+ return {};
10
+ }
11
+ export function cliOptionsTypes() {
12
+ return {
13
+ bump: String,
14
+ summary: String,
15
+ recursive: Boolean,
16
+ };
17
+ }
18
+ export const commandNames = ['change'];
19
+ export function help() {
20
+ return renderHelp({
21
+ description: 'Records a change intent: which packages a change affects, the bump type for each, and a summary that becomes the changelog entry. The intent file is written to .changeset/ in the changesets format.',
22
+ usages: [
23
+ 'pnpm change [--bump <type>] [--summary <text>] [<pkg>...]',
24
+ 'pnpm change status',
25
+ ],
26
+ descriptionLists: [
27
+ {
28
+ title: 'Options',
29
+ list: [
30
+ {
31
+ description: `Bump type for the named packages: ${BUMP_TYPES.join(', ')}. "none" records an explicit decline — the change needs no release`,
32
+ name: '--bump <type>',
33
+ },
34
+ {
35
+ description: 'The summary for the changelog entry. Runs non-interactively when given together with package names',
36
+ name: '--summary <text>',
37
+ },
38
+ ],
39
+ },
40
+ ],
41
+ });
42
+ }
43
+ export async function handler(opts, params) {
44
+ const workspaceDir = opts.workspaceDir;
45
+ if (!workspaceDir) {
46
+ throw new PnpmError('WORKSPACE_ONLY', 'pnpm change is only supported in a workspace');
47
+ }
48
+ // Only the exact no-option invocation is the status form, so a package
49
+ // that happens to be named "status" stays recordable.
50
+ if (params.length === 1 && params[0] === 'status' && opts.bump == null && opts.summary == null) {
51
+ return renderStatus(workspaceDir, opts);
52
+ }
53
+ return recordChange(workspaceDir, opts, params);
54
+ }
55
+ async function recordChange(workspaceDir, opts, params) {
56
+ const releasable = getReleasableProjects(opts.allProjects ?? [], workspaceDir, opts.versioning);
57
+ if (releasable.length === 0) {
58
+ throw new PnpmError('VERSIONING_NO_PACKAGES', 'No releasable packages found in this workspace');
59
+ }
60
+ const releasableDirs = new Set(releasable.map((project) => project.dir));
61
+ const refs = indexProjectRefs(opts.allProjects ?? [], workspaceDir);
62
+ for (const ref of params) {
63
+ const dirs = refs.refToDirs(ref);
64
+ if (dirs.length > 1) {
65
+ throw new PnpmError('VERSIONING_AMBIGUOUS_PACKAGE', `${ref} matches multiple workspace projects: ${dirs.map((dir) => `./${dir}`).join(', ')}. Reference the project by directory instead.`);
66
+ }
67
+ if (dirs.length === 0 || !releasableDirs.has(dirs[0])) {
68
+ throw new PnpmError('VERSIONING_UNKNOWN_PACKAGE', `${ref} is not a releasable package of this workspace`);
69
+ }
70
+ }
71
+ if (opts.bump != null && !BUMP_TYPES.includes(opts.bump)) {
72
+ throw new PnpmError('VERSIONING_INVALID_BUMP', `Invalid bump type: ${opts.bump}. Expected one of ${BUMP_TYPES.join(', ')}`);
73
+ }
74
+ const pkgRefs = params.length > 0
75
+ ? params
76
+ : await promptForPackages(releasable, workspaceDir, opts);
77
+ const releases = opts.bump != null
78
+ ? Object.fromEntries(pkgRefs.map((ref) => [ref, opts.bump]))
79
+ : await promptBumpTypes(pkgRefs);
80
+ const summary = opts.summary ??
81
+ await input({ message: 'Summary of the change (becomes the changelog entry):', required: true });
82
+ const id = await writeChangeIntent(workspaceDir, { releases, summary });
83
+ return `Recorded change intent .changeset/${id}.md`;
84
+ }
85
+ /**
86
+ * The affected-packages picker, changesets-style: the packages whose
87
+ * directories the branch touched are grouped first (under a "changed
88
+ * packages" heading) and preselected, the rest listed below under "unchanged
89
+ * packages". A name shared by several projects is offered under its directory
90
+ * reference so the written intent stays unambiguous. Change detection is
91
+ * best-effort — outside a git repo, or when no base branch can be found, the
92
+ * list falls back to a flat, unselected picker.
93
+ */
94
+ async function promptForPackages(releasable, workspaceDir, opts) {
95
+ const changedDirs = await detectChangedDirs(releasable, workspaceDir, opts);
96
+ const label = (project) => project.ref === project.name ? project.name : `${project.name} (./${project.dir})`;
97
+ let choices;
98
+ if (changedDirs.size > 0) {
99
+ const changed = releasable.filter((project) => changedDirs.has(project.dir));
100
+ const unchanged = releasable.filter((project) => !changedDirs.has(project.dir));
101
+ choices = [
102
+ new Separator('changed packages'),
103
+ ...changed.map((project) => ({ value: project.ref, name: label(project), checked: true })),
104
+ ...(unchanged.length > 0 ? [new Separator('unchanged packages')] : []),
105
+ ...unchanged.map((project) => ({ value: project.ref, name: label(project) })),
106
+ ];
107
+ }
108
+ else {
109
+ choices = releasable.map((project) => ({ value: project.ref, name: label(project) }));
110
+ }
111
+ return checkbox({
112
+ message: 'Which packages does this change affect?',
113
+ choices,
114
+ required: true,
115
+ });
116
+ }
117
+ /**
118
+ * The workspace-relative directories the current branch changed, relative to
119
+ * the base branch, using the same detection behind `--filter="[<ref>]"`.
120
+ * Returns an empty set on any failure so the picker degrades to a flat list.
121
+ */
122
+ async function detectChangedDirs(releasable, workspaceDir, opts) {
123
+ const baseCommit = await detectBaseCommit(workspaceDir);
124
+ if (baseCommit == null)
125
+ return new Set();
126
+ try {
127
+ const projectDirs = (opts.allProjects ?? []).map((project) => project.rootDir);
128
+ const [changedRootDirs] = await getChangedProjects(projectDirs, baseCommit, {
129
+ workspaceDir,
130
+ testPattern: opts.testPattern,
131
+ changedFilesIgnorePattern: opts.changedFilesIgnorePattern,
132
+ });
133
+ const releasableDirs = new Set(releasable.map((project) => project.dir));
134
+ return new Set(changedRootDirs
135
+ .map((rootDir) => toProjectDir(workspaceDir, rootDir))
136
+ .filter((dir) => releasableDirs.has(dir)));
137
+ }
138
+ catch {
139
+ return new Set();
140
+ }
141
+ }
142
+ /** The merge-base of HEAD with the default branch, or `undefined`. */
143
+ async function detectBaseCommit(cwd) {
144
+ for (const branch of ['main', 'master']) {
145
+ try {
146
+ // eslint-disable-next-line no-await-in-loop
147
+ const { stdout } = await execa('git', ['merge-base', 'HEAD', branch], { cwd });
148
+ const commit = String(stdout).trim();
149
+ if (commit !== '')
150
+ return commit;
151
+ }
152
+ catch {
153
+ // Try the next candidate branch.
154
+ }
155
+ }
156
+ return undefined;
157
+ }
158
+ /**
159
+ * The changesets-style bump picker: ask which packages get a major bump, then
160
+ * which of the rest get a minor, and default whatever remains to patch. One
161
+ * multiselect per level reads far better than a per-package prompt when many
162
+ * packages are affected.
163
+ */
164
+ async function promptBumpTypes(pkgRefs) {
165
+ const bumpByRef = new Map();
166
+ let remaining = [...pkgRefs];
167
+ for (const bumpType of ['major', 'minor']) {
168
+ if (remaining.length === 0)
169
+ break;
170
+ // eslint-disable-next-line no-await-in-loop
171
+ const chosen = new Set(await checkbox({
172
+ message: `Which packages should have a ${bumpType} bump?`,
173
+ choices: remaining.map((ref) => ({ value: ref })),
174
+ }));
175
+ for (const ref of chosen)
176
+ bumpByRef.set(ref, bumpType);
177
+ remaining = remaining.filter((ref) => !chosen.has(ref));
178
+ }
179
+ for (const ref of remaining)
180
+ bumpByRef.set(ref, 'patch');
181
+ // Emit in the original selection order rather than grouped by bump level.
182
+ return Object.fromEntries(pkgRefs.map((ref) => [ref, bumpByRef.get(ref)]));
183
+ }
184
+ async function renderStatus(workspaceDir, opts) {
185
+ const intents = await readChangeIntents(workspaceDir);
186
+ const ledger = await readLedger(workspaceDir);
187
+ const plan = assembleReleasePlan({
188
+ workspaceDir,
189
+ projects: toWorkspaceProjects(opts.allProjects ?? []),
190
+ intents,
191
+ ledger,
192
+ versioning: opts.versioning,
193
+ });
194
+ if (plan.releases.length === 0) {
195
+ return 'No pending changes.';
196
+ }
197
+ const consumedIds = new Set(plan.releases.flatMap((release) => release.intents.map((intent) => intent.id)));
198
+ let output = 'Pending change intents:\n';
199
+ for (const intent of intents.filter(({ id }) => consumedIds.has(id))) {
200
+ output += ` .changeset/${intent.id}.md\n`;
201
+ }
202
+ output += '\n';
203
+ output += renderReleasePlan(plan);
204
+ return output;
205
+ }
206
+ export function renderReleasePlan(plan) {
207
+ let output = 'Release plan:\n';
208
+ for (const release of plan.releases) {
209
+ output += ` ${release.name}: ${release.currentVersion} → ${release.newVersion} (${release.bumpType}, via ${release.causes.join('+')})\n`;
210
+ }
211
+ return output;
212
+ }
213
+ /**
214
+ * The projects a change intent may demand a release from: named, carrying a
215
+ * valid semver version, and not frozen by `versioning.ignore`. Matches the
216
+ * participant set of the release-plan assembler.
217
+ */
218
+ export function getReleasableProjects(allProjects, workspaceDir, versioning) {
219
+ const refs = indexProjectRefs(allProjects, workspaceDir);
220
+ const ignoredDirs = new Set((versioning?.ignore ?? []).flatMap((ref) => refs.refToDirs(ref)));
221
+ return allProjects
222
+ .filter(({ manifest }) => manifest.name != null &&
223
+ manifest.version != null &&
224
+ valid(manifest.version) != null)
225
+ .map((project) => ({ name: project.manifest.name, dir: toProjectDir(workspaceDir, project.rootDir) }))
226
+ .filter(({ dir }) => !ignoredDirs.has(dir))
227
+ .map(({ name, dir }) => ({
228
+ name,
229
+ dir,
230
+ ref: refs.nameToDirs(name).length > 1 ? `./${dir}` : name,
231
+ }))
232
+ .sort((left, right) => left.ref.localeCompare(right.ref));
233
+ }
234
+ export function toWorkspaceProjects(allProjects) {
235
+ return allProjects.map((project) => ({ rootDir: project.rootDir, manifest: project.manifest }));
236
+ }
237
+ export const change = {
238
+ handler,
239
+ help,
240
+ commandNames,
241
+ cliOptionsTypes,
242
+ rcOptionsTypes,
243
+ recursiveByDefault: true,
244
+ };
245
+ //# sourceMappingURL=index.js.map
@@ -1,8 +1,11 @@
1
1
  import type { LockfileObject } from '@pnpm/lockfile.types';
2
- import type { PnpmSettings, Project, ProjectId, ProjectManifest } from '@pnpm/types';
2
+ import type { DependenciesField, PnpmSettings, Project, ProjectId, ProjectManifest } from '@pnpm/types';
3
3
  export interface CreateDeployFilesOptions {
4
4
  allProjects: Array<Pick<Project, 'manifest' | 'rootDirRealPath'>>;
5
5
  deployDir: string;
6
+ include: {
7
+ [dependenciesField in DependenciesField]: boolean;
8
+ };
6
9
  lockfile: LockfileObject;
7
10
  lockfileDir: string;
8
11
  patchedDependencies?: PnpmSettings['patchedDependencies'];
@@ -20,4 +23,4 @@ export interface DeployFiles {
20
23
  manifest: ProjectManifest;
21
24
  workspaceManifest?: DeployWorkspaceManifest;
22
25
  }
23
- export declare function createDeployFiles({ allProjects, deployDir, lockfile, lockfileDir, patchedDependencies, selectedProjectManifest, projectId, rootProjectManifestDir, allowBuilds, }: CreateDeployFilesOptions): DeployFiles;
26
+ export declare function createDeployFiles({ allProjects, deployDir, include, lockfile, lockfileDir, patchedDependencies, selectedProjectManifest, projectId, rootProjectManifestDir, allowBuilds, }: CreateDeployFilesOptions): DeployFiles;
@@ -3,7 +3,7 @@ import url from 'node:url';
3
3
  import * as dp from '@pnpm/deps.path';
4
4
  import normalizePath from 'normalize-path';
5
5
  const DEPENDENCIES_FIELD = ['dependencies', 'devDependencies', 'optionalDependencies'];
6
- export function createDeployFiles({ allProjects, deployDir, lockfile, lockfileDir, patchedDependencies, selectedProjectManifest, projectId, rootProjectManifestDir, allowBuilds, }) {
6
+ export function createDeployFiles({ allProjects, deployDir, include, lockfile, lockfileDir, patchedDependencies, selectedProjectManifest, projectId, rootProjectManifestDir, allowBuilds, }) {
7
7
  const deployedProjectRealPath = path.resolve(lockfileDir, projectId);
8
8
  const inputSnapshot = lockfile.importers[projectId];
9
9
  const targetSnapshot = {
@@ -61,13 +61,17 @@ export function createDeployFiles({ allProjects, deployDir, lockfile, lockfileDi
61
61
  targetSpecifiers[name] = targetDependencies[name] = version;
62
62
  continue;
63
63
  }
64
+ resolveResult.packageName ??= name;
64
65
  targetSpecifiers[name] = targetDependencies[name] =
65
66
  resolveResult.resolvedPath === deployedProjectRealPath ? 'link:.' : createFileUrlDepPath(resolveResult, allProjects);
66
67
  }
67
68
  }
69
+ const deployPackageSnapshots = filterDeployPackageSnapshots(targetSnapshot, targetPackageSnapshots, include);
68
70
  const result = {
69
71
  lockfile: {
70
72
  ...lockfile,
73
+ // The deployed manifest contains concrete versions, and catalogs are not copied to the target.
74
+ catalogs: undefined,
71
75
  patchedDependencies: undefined,
72
76
  overrides: undefined, // the effects of the overrides should already be part of the package snapshots
73
77
  packageExtensionsChecksum: undefined, // the effects of the package extensions should already be part of the package snapshots
@@ -79,7 +83,7 @@ export function createDeployFiles({ allProjects, deployDir, lockfile, lockfileDi
79
83
  importers: {
80
84
  ['.']: targetSnapshot,
81
85
  },
82
- packages: targetPackageSnapshots,
86
+ packages: deployPackageSnapshots,
83
87
  },
84
88
  manifest: {
85
89
  ...selectedProjectManifest,
@@ -109,6 +113,37 @@ export function createDeployFiles({ allProjects, deployDir, lockfile, lockfileDi
109
113
  }
110
114
  return result;
111
115
  }
116
+ function filterDeployPackageSnapshots(importer, packages, include) {
117
+ const queue = [];
118
+ const enqueue = (dependencies) => {
119
+ for (const [alias, reference] of Object.entries(dependencies ?? {})) {
120
+ const depPath = dp.refToRelative(reference, alias);
121
+ if (depPath != null && packages[depPath] != null)
122
+ queue.push(depPath);
123
+ }
124
+ };
125
+ if (include.dependencies)
126
+ enqueue(importer.dependencies);
127
+ if (include.devDependencies)
128
+ enqueue(importer.devDependencies);
129
+ if (include.optionalDependencies)
130
+ enqueue(importer.optionalDependencies);
131
+ const reachable = new Set();
132
+ let head = 0;
133
+ while (head < queue.length) {
134
+ const depPath = queue[head++];
135
+ if (reachable.has(depPath))
136
+ continue;
137
+ reachable.add(depPath);
138
+ const snapshot = packages[depPath];
139
+ if (snapshot == null)
140
+ continue;
141
+ enqueue(snapshot.dependencies);
142
+ if (include.optionalDependencies)
143
+ enqueue(snapshot.optionalDependencies);
144
+ }
145
+ return Object.fromEntries(Array.from(reachable, (depPath) => [depPath, packages[depPath]]));
146
+ }
112
147
  function convertPackageSnapshot(inputSnapshot, opts) {
113
148
  const inputResolution = inputSnapshot.resolution;
114
149
  let outputResolution;
@@ -178,6 +213,7 @@ function convertResolvedDependencies(input, opts) {
178
213
  output[key] = 'link:.'; // the path is relative to the lockfile dir, which means '.' would reference the deploy dir
179
214
  continue;
180
215
  }
216
+ resolveResult.packageName ??= key;
181
217
  output[key] = createFileUrlDepPath(resolveResult, opts.allProjects);
182
218
  }
183
219
  return output;
@@ -194,7 +230,7 @@ function resolveLinkOrFile(pkgVer, opts) {
194
230
  const resolveSchemeResult = resolveScheme('file:', lockfileDir) ?? resolveScheme('link:', projectRootDirRealPath);
195
231
  if (resolveSchemeResult)
196
232
  return resolveSchemeResult;
197
- const { nonSemverVersion, patchHash, peerDepGraphHash, version } = dp.parse(pkgVer);
233
+ const { name, nonSemverVersion, patchHash, peerDepGraphHash, version } = dp.parse(pkgVer);
198
234
  if (!nonSemverVersion)
199
235
  return undefined;
200
236
  if (version) {
@@ -207,12 +243,13 @@ function resolveLinkOrFile(pkgVer, opts) {
207
243
  throw new Error(`Something goes wrong, suffix should be undefined but isn't: ${parseResult.suffix}`);
208
244
  }
209
245
  parseResult.suffix = `${patchHash ?? ''}${peerDepGraphHash ?? ''}`;
246
+ parseResult.packageName = name;
210
247
  return parseResult;
211
248
  }
212
- function createFileUrlDepPath({ resolvedPath, suffix }, allProjects) {
249
+ function createFileUrlDepPath({ resolvedPath, suffix, packageName }, allProjects) {
213
250
  const depFileUrl = url.pathToFileURL(resolvedPath).toString();
214
251
  const project = allProjects.find(project => project.rootDirRealPath === resolvedPath);
215
- const name = project?.manifest.name ?? path.basename(resolvedPath);
252
+ const name = project?.manifest.name ?? packageName ?? path.basename(resolvedPath);
216
253
  return `${name}@${depFileUrl}${suffix ?? ''}`;
217
254
  }
218
255
  //# sourceMappingURL=createDeployFiles.js.map
@@ -333,6 +333,11 @@ async function deployFromSharedLockfile(opts, selectedProject, deployDir) {
333
333
  const deployFiles = createDeployFiles({
334
334
  allProjects,
335
335
  deployDir,
336
+ include: {
337
+ dependencies: opts.production !== false,
338
+ devDependencies: opts.dev !== false,
339
+ optionalDependencies: opts.optional !== false,
340
+ },
336
341
  lockfile,
337
342
  lockfileDir,
338
343
  patchedDependencies: opts.patchedDependencies,
package/lib/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
+ export { change } from './change/index.js';
1
2
  export { deploy } from './deploy/index.js';
3
+ export { lane } from './lane/index.js';
2
4
  export { packApp } from './pack-app/index.js';
3
5
  export { pack, publish } from './publish/index.js';
4
6
  export * as stage from './stage/index.js';
package/lib/index.js CHANGED
@@ -1,4 +1,6 @@
1
+ export { change } from './change/index.js';
1
2
  export { deploy } from './deploy/index.js';
3
+ export { lane } from './lane/index.js';
2
4
  export { packApp } from './pack-app/index.js';
3
5
  export { pack, publish } from './publish/index.js';
4
6
  export * as stage from './stage/index.js';
@@ -0,0 +1,24 @@
1
+ import type { Config } from '@pnpm/config.reader';
2
+ import type { Project, ProjectsGraph } from '@pnpm/types';
3
+ export declare function rcOptionsTypes(): Record<string, unknown>;
4
+ export declare function cliOptionsTypes(): Record<string, unknown>;
5
+ export declare const commandNames: string[];
6
+ /**
7
+ * The reserved name of the default lane: every package is on it unless
8
+ * assigned elsewhere, packages on it release stable versions, and no
9
+ * prerelease lane can take the name.
10
+ */
11
+ export declare const MAIN_LANE = "main";
12
+ export declare function help(): string;
13
+ export type LaneCommandOptions = Pick<Config, 'dir' | 'filter' | 'versioning' | 'workspaceDir'> & {
14
+ allProjects?: Project[];
15
+ selectedProjectsGraph?: ProjectsGraph;
16
+ };
17
+ export declare function handler(opts: LaneCommandOptions, params: string[]): Promise<string>;
18
+ export declare const lane: {
19
+ handler: typeof handler;
20
+ help: typeof help;
21
+ commandNames: string[];
22
+ cliOptionsTypes: typeof cliOptionsTypes;
23
+ rcOptionsTypes: typeof rcOptionsTypes;
24
+ };