@pnpm/releasing.commands 1100.7.1 → 1100.8.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,40 @@
1
1
  # @pnpm/releasing.commands
2
2
 
3
+ ## 1100.8.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Added support for `publishConfig.name`, which publishes a package under a different name than the one its manifest carries in the workspace. It is for a project whose published name is already taken by a sibling project, which otherwise has to be renamed by a build step just before publishing. Only the published artifact is renamed — dependents, `pnpm-lock.yaml`, and release tooling keep addressing the project by its manifest name — and the new name reaches the packed manifest, the tarball filename, and everything that addresses the package at the registry: the already-published check of `pnpm publish -r`, its registry selection, and the release-planning probes of `pnpm change status` and `pnpm version -r` [#13345](https://github.com/pnpm/pnpm/issues/13345).
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies:
12
+ - @pnpm/bins.resolver@1100.0.12
13
+ - @pnpm/cli.utils@1101.0.20
14
+ - @pnpm/config.pick-registry-for-package@1100.0.13
15
+ - @pnpm/config.reader@1101.15.0
16
+ - @pnpm/deps.path@1100.0.12
17
+ - @pnpm/engine.runtime.commands@1100.1.17
18
+ - @pnpm/engine.runtime.node-resolver@1101.1.19
19
+ - @pnpm/exec.lifecycle@1100.1.9
20
+ - @pnpm/fetching.directory-fetcher@1100.0.26
21
+ - @pnpm/fs.indexed-pkg-importer@1100.0.22
22
+ - @pnpm/installing.client@1100.3.0
23
+ - @pnpm/installing.commands@1100.12.1
24
+ - @pnpm/lockfile.fs@1100.1.15
25
+ - @pnpm/lockfile.types@1100.0.17
26
+ - @pnpm/network.auth-header@1101.1.7
27
+ - @pnpm/network.fetch@1100.1.9
28
+ - @pnpm/releasing.exportable-manifest@1100.2.0
29
+ - @pnpm/releasing.versioning@1100.2.2
30
+ - @pnpm/resolving.npm-resolver@1102.1.9
31
+ - @pnpm/resolving.registry.types@1100.1.7
32
+ - @pnpm/resolving.resolver-base@1100.5.5
33
+ - @pnpm/types@1101.7.0
34
+ - @pnpm/workspace.projects-filter@1100.0.33
35
+ - @pnpm/workspace.projects-sorter@1100.0.12
36
+ - @pnpm/workspace.workspace-manifest-writer@1100.0.19
37
+
3
38
  ## 1100.7.1
4
39
 
5
40
  ### Patch Changes
@@ -5,6 +5,7 @@ import { getChangedProjects } from '@pnpm/workspace.projects-filter';
5
5
  import { safeExeca as execa } from 'execa';
6
6
  import { renderHelp } from 'render-help';
7
7
  import { valid } from 'semver';
8
+ import { publishedNameByManifestName } from '../publishedNames.js';
8
9
  import { resolveUnpublishedDirs } from '../resolveUnpublishedDirs.js';
9
10
  export function rcOptionsTypes() {
10
11
  return {};
@@ -192,7 +193,8 @@ async function renderStatus(workspaceDir, opts) {
192
193
  ledger,
193
194
  versioning: opts.versioning,
194
195
  };
195
- const unpublishedDirs = await resolveUnpublishedDirs(assembleReleasePlan(baseArgs), opts);
196
+ const publishedNames = publishedNameByManifestName(baseArgs.projects);
197
+ const unpublishedDirs = await resolveUnpublishedDirs(assembleReleasePlan(baseArgs), { ...opts, publishedNames });
196
198
  const plan = assembleReleasePlan({ ...baseArgs, unpublishedDirs });
197
199
  if (plan.releases.length === 0) {
198
200
  return 'No pending changes.';
@@ -19,6 +19,7 @@ import { renderHelp } from 'render-help';
19
19
  import tar from 'tar-stream';
20
20
  import { glob } from 'tinyglobby';
21
21
  import validateNpmPackageName from 'validate-npm-package-name';
22
+ import { normalizePackageName } from '../tarball/safeTarballFilename.js';
22
23
  import { fetchPreviousChangelog } from './previousChangelog.js';
23
24
  import { runScriptsIfPresent } from './publish.js';
24
25
  const LICENSE_GLOB = 'LICEN{S,C}E{,.*}'; // cspell:disable-line
@@ -202,7 +203,17 @@ export async function api(opts) {
202
203
  publishManifest.version = stripBuildMetadata(publishManifest.version);
203
204
  let tarballName;
204
205
  let packDestination;
205
- const normalizedName = manifest.name.replace('@', '').replace('/', '-');
206
+ // Read back off the publish manifest so a `publishConfig.name` rename reaches
207
+ // the filename too — the tarball name, the packed manifest, and the registry
208
+ // metadata all name one artifact. The rename never went through the check on
209
+ // `manifest.name` above, so it is validated here: it lands in the tarball
210
+ // filename, where a separator would smuggle path components into the join and
211
+ // write outside the pack destination.
212
+ const publishedName = publishManifest.name || manifest.name;
213
+ if (!validateNpmPackageName(publishedName).validForOldPackages) {
214
+ throw new PnpmError('INVALID_PACKAGE_NAME', `Invalid package name "${publishedName}".`);
215
+ }
216
+ const normalizedName = normalizePackageName(publishedName);
206
217
  if (opts.out) {
207
218
  if (opts.packDestination) {
208
219
  throw new PnpmError('INVALID_OPTION', 'Cannot use --pack-destination and --out together');
@@ -241,7 +252,7 @@ export async function api(opts) {
241
252
  // composed here on top of the previously published version's changelog and
242
253
  // packed in. A composed entry supersedes any stale committed CHANGELOG.md.
243
254
  const injectedEntries = {};
244
- const composedChangelog = await composeRegistryChangelog(opts, manifest.name, manifest.version);
255
+ const composedChangelog = await composeRegistryChangelog(opts, manifest.name, publishedName, manifest.version);
245
256
  if (composedChangelog != null) {
246
257
  delete filesMap['package/CHANGELOG.md'];
247
258
  injectedEntries['package/CHANGELOG.md'] = composedChangelog;
@@ -334,16 +345,22 @@ function isManifestEntry(name) {
334
345
  * `repository`, there is no workspace, or the release has no parked section
335
346
  * (an ordinary `pnpm pack` of a package that is not mid-release).
336
347
  */
337
- async function composeRegistryChangelog(opts, pkgName, version) {
348
+ /**
349
+ * `pkgName` keys the parked section — the workspace, the ledger, and every
350
+ * intent address the project by its manifest name. `publishedName` is the only
351
+ * name the registry knows, so it selects the previous changelog to build on and
352
+ * titles the composed one.
353
+ */
354
+ async function composeRegistryChangelog(opts, pkgName, publishedName, version) {
338
355
  if (changelogStorage(opts.versioning) !== 'registry' || opts.workspaceDir == null)
339
356
  return undefined;
340
357
  const section = await readPendingChangelog(opts.workspaceDir, pkgName, version);
341
358
  if (section == null)
342
359
  return undefined;
343
360
  const previous = opts.registries != null
344
- ? await fetchPreviousChangelog(opts, pkgName, version)
361
+ ? await fetchPreviousChangelog(opts, publishedName, version)
345
362
  : undefined;
346
- return renderChangelog(previous ?? null, pkgName, section);
363
+ return renderChangelog(previous ?? null, publishedName, section);
347
364
  }
348
365
  function stripBuildMetadata(version) {
349
366
  const plusIndex = version.indexOf('+');
@@ -6,6 +6,7 @@ import { sortFilteredProjects } from '@pnpm/workspace.projects-sorter';
6
6
  import pFilter from 'p-filter';
7
7
  import { pick } from 'ramda';
8
8
  import { writeJsonFile } from 'write-json-file';
9
+ import { publishedName } from '../publishedNames.js';
9
10
  import { batchPublishPackages } from './batchPublish.js';
10
11
  import { publish } from './publish.js';
11
12
  export async function recursivePublish(opts) {
@@ -31,7 +32,7 @@ export async function recursivePublish(opts) {
31
32
  lockfileDir: opts.lockfileDir ?? pkg.rootDir,
32
33
  registries: opts.registries,
33
34
  resolve,
34
- }, pkg.manifest.name, pkg.manifest.version));
35
+ }, publishedName(pkg.manifest), pkg.manifest.version));
35
36
  });
36
37
  const publishedPkgDirs = new Set(pkgsToPublish.map(({ rootDir }) => rootDir));
37
38
  const publishedPackages = [];
@@ -75,7 +76,9 @@ export async function recursivePublish(opts) {
75
76
  if (!publishedPkgDirs.has(pkgDir))
76
77
  continue;
77
78
  const pkg = opts.selectedProjectsGraph[pkgDir].package;
78
- const registry = pkg.manifest.publishConfig?.registry ?? pickRegistryForPackage(opts.registries, pkg.manifest.name);
79
+ // The registry is picked by scope, so a `publishConfig.name` that
80
+ // moves the package to another scope has to route by the new one.
81
+ const registry = pkg.manifest.publishConfig?.registry ?? pickRegistryForPackage(opts.registries, publishedName(pkg.manifest));
79
82
  // eslint-disable-next-line no-await-in-loop
80
83
  const publishResult = await publish({
81
84
  ...opts,
@@ -0,0 +1,18 @@
1
+ import type { WorkspaceProject } from '@pnpm/releasing.versioning';
2
+ import type { BaseManifest } from '@pnpm/types';
3
+ /**
4
+ * The name a manifest publishes under — its `publishConfig.name` rename, or
5
+ * the name it carries in the workspace.
6
+ *
7
+ * The workspace — and so every parked changelog section, the ledger, every
8
+ * intent, and every dependent — keys on the manifest name, while the registry
9
+ * only ever sees the published one. Anything that addresses a project *at the
10
+ * registry* has to resolve the name through here first.
11
+ */
12
+ export declare function publishedName(manifest: Pick<BaseManifest, 'name' | 'publishConfig'>): string | undefined;
13
+ /**
14
+ * Manifest name → published name, for every project that renames itself.
15
+ * Projects that publish under their manifest name are absent, so a lookup miss
16
+ * means "no rename". For call sites that hold a name rather than a manifest.
17
+ */
18
+ export declare function publishedNameByManifestName(projects: WorkspaceProject[]): Map<string, string>;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The name a manifest publishes under — its `publishConfig.name` rename, or
3
+ * the name it carries in the workspace.
4
+ *
5
+ * The workspace — and so every parked changelog section, the ledger, every
6
+ * intent, and every dependent — keys on the manifest name, while the registry
7
+ * only ever sees the published one. Anything that addresses a project *at the
8
+ * registry* has to resolve the name through here first.
9
+ */
10
+ export function publishedName(manifest) {
11
+ const renamed = manifest.publishConfig?.name;
12
+ return typeof renamed === 'string' && renamed !== '' ? renamed : manifest.name;
13
+ }
14
+ /**
15
+ * Manifest name → published name, for every project that renames itself.
16
+ * Projects that publish under their manifest name are absent, so a lookup miss
17
+ * means "no rename". For call sites that hold a name rather than a manifest.
18
+ */
19
+ export function publishedNameByManifestName(projects) {
20
+ const renames = new Map();
21
+ for (const { manifest } of projects) {
22
+ const published = publishedName(manifest);
23
+ if (manifest.name && published != null && published !== manifest.name) {
24
+ renames.set(manifest.name, published);
25
+ }
26
+ }
27
+ return renames;
28
+ }
29
+ //# sourceMappingURL=publishedNames.js.map
@@ -5,6 +5,14 @@ export type UnpublishedProbeOptions = PreviousChangelogOptions & {
5
5
  /** Overridable for tests; production probes the registry. */
6
6
  checkVersionPublished?: CheckVersionPublished;
7
7
  networkConcurrency?: number;
8
+ /** Manifest name → published name, from `publishedNameByManifestName`. */
9
+ publishedNames?: ReadonlyMap<string, string>;
8
10
  };
9
- /** The releases in `plan` whose current version the registry does not have — {@link assembleReleasePlan}'s `unpublishedDirs`. */
11
+ /**
12
+ * The releases in `plan` whose current version the registry does not have — {@link assembleReleasePlan}'s `unpublishedDirs`.
13
+ *
14
+ * A release is keyed by its manifest name, so `publishedNames` translates it
15
+ * for the probe; without that a renamed project reads as never published and
16
+ * debuts at its manifest version on every release.
17
+ */
10
18
  export declare function resolveUnpublishedDirs(plan: ReleasePlan, opts: UnpublishedProbeOptions): Promise<Set<string>>;
@@ -1,13 +1,19 @@
1
1
  import pLimit from 'p-limit';
2
2
  import { createVersionPublishedChecker } from './publish/previousChangelog.js';
3
3
  const DEFAULT_NETWORK_CONCURRENCY = 16;
4
- /** The releases in `plan` whose current version the registry does not have — {@link assembleReleasePlan}'s `unpublishedDirs`. */
4
+ /**
5
+ * The releases in `plan` whose current version the registry does not have — {@link assembleReleasePlan}'s `unpublishedDirs`.
6
+ *
7
+ * A release is keyed by its manifest name, so `publishedNames` translates it
8
+ * for the probe; without that a renamed project reads as never published and
9
+ * debuts at its manifest version on every release.
10
+ */
5
11
  export async function resolveUnpublishedDirs(plan, opts) {
6
12
  const checkVersionPublished = opts.checkVersionPublished ?? createVersionPublishedChecker(opts);
7
13
  const limit = pLimit(opts.networkConcurrency ?? DEFAULT_NETWORK_CONCURRENCY);
8
14
  const probed = await Promise.all(plan.releases.map((release) => limit(async () => ({
9
15
  dir: release.dir,
10
- published: await checkVersionPublished(release.name, release.currentVersion),
16
+ published: await checkVersionPublished(opts.publishedNames?.get(release.name) ?? release.name, release.currentVersion),
11
17
  }))));
12
18
  return new Set(probed.filter(({ published }) => !published).map(({ dir }) => dir));
13
19
  }
@@ -11,6 +11,7 @@ import { renderHelp } from 'render-help';
11
11
  import { inc, valid } from 'semver';
12
12
  import { renderReleasePlan, toWorkspaceProjects } from '../change/index.js';
13
13
  import { changelogHasSection, fetchPublishedChangelog } from '../publish/previousChangelog.js';
14
+ import { publishedNameByManifestName } from '../publishedNames.js';
14
15
  import { resolveUnpublishedDirs } from '../resolveUnpublishedDirs.js';
15
16
  export function rcOptionsTypes() {
16
17
  return pick([
@@ -182,14 +183,15 @@ async function releaseFromIntents(opts) {
182
183
  filter,
183
184
  enforceWorkspaceProtocol: true,
184
185
  };
185
- const unpublishedDirs = await resolveUnpublishedDirs(assembleReleasePlan(baseArgs), opts);
186
+ const publishedNames = publishedNameByManifestName(projects);
187
+ const unpublishedDirs = await resolveUnpublishedDirs(assembleReleasePlan(baseArgs), { ...opts, publishedNames });
186
188
  const plan = assembleReleasePlan({ ...baseArgs, unpublishedDirs });
187
189
  const applyOpts = {
188
190
  workspaceDir,
189
191
  projects,
190
192
  allIntents: intents,
191
193
  versioning: opts.versioning,
192
- verifyPublished: buildVerifyPublished(opts),
194
+ verifyPublished: buildVerifyPublished(opts, publishedNames),
193
195
  };
194
196
  if (plan.releases.length === 0) {
195
197
  // A full (unfiltered) run garbage-collects the intent files an empty plan
@@ -223,12 +225,14 @@ async function releaseFromIntents(opts) {
223
225
  * is kept. `undefined` in `repository` storage, where the committed changelog
224
226
  * makes the ledger alone sufficient.
225
227
  */
226
- function buildVerifyPublished(opts) {
228
+ function buildVerifyPublished(opts, publishedNames) {
227
229
  if (changelogStorage(opts.versioning) !== 'registry')
228
230
  return undefined;
229
231
  return async (name, version, section) => {
230
232
  try {
231
- const changelog = await fetchPublishedChangelog(opts, name, version);
233
+ // The parked section is keyed by the manifest name, which is what the
234
+ // ledger joins on; the registry only knows the published one.
235
+ const changelog = await fetchPublishedChangelog(opts, publishedNames.get(name) ?? name, version);
232
236
  return changelog != null && changelogHasSection(changelog, section);
233
237
  }
234
238
  catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/releasing.commands",
3
- "version": "1100.7.1",
3
+ "version": "1100.8.0",
4
4
  "description": "Commands for deploy, pack, and publish",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -27,46 +27,46 @@
27
27
  "!*.map"
28
28
  ],
29
29
  "dependencies": {
30
- "@inquirer/prompts": "^8.4.3",
31
- "@pnpm/bins.resolver": "1100.0.11",
30
+ "@inquirer/prompts": "^8.5.2",
31
+ "@pnpm/bins.resolver": "1100.0.12",
32
32
  "@pnpm/catalogs.types": "1100.0.1",
33
33
  "@pnpm/cli.common-cli-options-help": "1100.0.3",
34
- "@pnpm/cli.utils": "1101.0.19",
35
- "@pnpm/config.pick-registry-for-package": "1100.0.12",
36
- "@pnpm/config.reader": "1101.14.0",
34
+ "@pnpm/cli.utils": "1101.0.20",
35
+ "@pnpm/config.pick-registry-for-package": "1100.0.13",
36
+ "@pnpm/config.reader": "1101.15.0",
37
37
  "@pnpm/constants": "1100.0.1",
38
- "@pnpm/deps.path": "1100.0.11",
39
- "@pnpm/engine.runtime.commands": "1100.1.16",
40
- "@pnpm/engine.runtime.node-resolver": "1101.1.18",
38
+ "@pnpm/deps.path": "1100.0.12",
39
+ "@pnpm/engine.runtime.commands": "1100.1.17",
40
+ "@pnpm/engine.runtime.node-resolver": "1101.1.19",
41
41
  "@pnpm/error": "1100.1.0",
42
- "@pnpm/exec.lifecycle": "1100.1.8",
42
+ "@pnpm/exec.lifecycle": "1100.1.9",
43
43
  "@pnpm/exec.pnpm-cli-runner": "1100.0.2",
44
- "@pnpm/fetching.directory-fetcher": "1100.0.25",
44
+ "@pnpm/fetching.directory-fetcher": "1100.0.26",
45
45
  "@pnpm/fetching.types": "1100.0.3",
46
- "@pnpm/fs.indexed-pkg-importer": "1100.0.21",
46
+ "@pnpm/fs.indexed-pkg-importer": "1100.0.22",
47
47
  "@pnpm/fs.is-empty-dir-or-nothing": "1100.0.1",
48
48
  "@pnpm/fs.packlist": "1100.0.4",
49
- "@pnpm/installing.client": "1100.2.20",
50
- "@pnpm/installing.commands": "1100.12.0",
51
- "@pnpm/lockfile.fs": "1100.1.14",
52
- "@pnpm/lockfile.types": "1100.0.16",
53
- "@pnpm/network.auth-header": "1101.1.6",
54
- "@pnpm/network.fetch": "1100.1.8",
49
+ "@pnpm/installing.client": "1100.3.0",
50
+ "@pnpm/installing.commands": "1100.12.1",
51
+ "@pnpm/lockfile.fs": "1100.1.15",
52
+ "@pnpm/lockfile.types": "1100.0.17",
53
+ "@pnpm/network.auth-header": "1101.1.7",
54
+ "@pnpm/network.fetch": "1100.1.9",
55
55
  "@pnpm/network.git-utils": "1100.0.3",
56
56
  "@pnpm/network.web-auth": "1101.3.0",
57
57
  "@pnpm/npm-package-arg": "^2.0.0",
58
- "@pnpm/releasing.exportable-manifest": "1100.1.15",
59
- "@pnpm/releasing.versioning": "1100.2.1",
60
- "@pnpm/resolving.npm-resolver": "1102.1.8",
61
- "@pnpm/resolving.registry.types": "1100.1.6",
62
- "@pnpm/resolving.resolver-base": "1100.5.4",
63
- "@pnpm/types": "1101.6.0",
64
- "@pnpm/workspace.projects-filter": "1100.0.32",
65
- "@pnpm/workspace.projects-sorter": "1100.0.11",
66
- "@pnpm/workspace.workspace-manifest-writer": "1100.0.18",
58
+ "@pnpm/releasing.exportable-manifest": "1100.2.0",
59
+ "@pnpm/releasing.versioning": "1100.2.2",
60
+ "@pnpm/resolving.npm-resolver": "1102.1.9",
61
+ "@pnpm/resolving.registry.types": "1100.1.7",
62
+ "@pnpm/resolving.resolver-base": "1100.5.5",
63
+ "@pnpm/types": "1101.7.0",
64
+ "@pnpm/workspace.projects-filter": "1100.0.33",
65
+ "@pnpm/workspace.projects-sorter": "1100.0.12",
66
+ "@pnpm/workspace.workspace-manifest-writer": "1100.0.19",
67
67
  "@types/normalize-path": "^3.0.2",
68
68
  "@zkochan/rimraf": "^4.0.0",
69
- "chalk": "^5.6.2",
69
+ "chalk": "^6.0.0",
70
70
  "ci-info": "^4.4.0",
71
71
  "detect-libc": "^2.1.2",
72
72
  "execa": "npm:safe-execa@0.3.0",
@@ -76,14 +76,14 @@
76
76
  "normalize-registry-url": "2.0.1",
77
77
  "npm-registry-fetch": "^19.0.0",
78
78
  "p-filter": "^4.1.0",
79
- "p-limit": "^7.3.0",
79
+ "p-limit": "^7.3.1",
80
80
  "ramda": "npm:@pnpm/ramda@0.28.1",
81
81
  "realpath-missing": "^2.0.0",
82
82
  "render-help": "^2.0.0",
83
- "semver": "^7.8.4",
83
+ "semver": "^7.8.5",
84
84
  "tar-stream": "^3.2.0",
85
85
  "tempy": "3.0.0",
86
- "tinyglobby": "^0.2.16",
86
+ "tinyglobby": "^0.2.17",
87
87
  "validate-npm-package-name": "7.0.2",
88
88
  "write-json-file": "^7.0.0",
89
89
  "write-yaml-file": "^6.0.0"
@@ -93,12 +93,12 @@
93
93
  },
94
94
  "devDependencies": {
95
95
  "@jest/globals": "30.4.1",
96
- "@pnpm/assert-project": "1100.0.22",
96
+ "@pnpm/assert-project": "1100.0.23",
97
97
  "@pnpm/catalogs.config": "1100.0.3",
98
- "@pnpm/hooks.pnpmfile": "1100.0.22",
98
+ "@pnpm/hooks.pnpmfile": "1100.0.23",
99
99
  "@pnpm/logger": "1100.0.0",
100
- "@pnpm/prepare": "1100.0.22",
101
- "@pnpm/releasing.commands": "1100.7.1",
100
+ "@pnpm/prepare": "1100.0.23",
101
+ "@pnpm/releasing.commands": "1100.8.0",
102
102
  "@pnpm/test-fixtures": "1100.0.1",
103
103
  "@pnpm/test-ipc-server": "1100.0.0",
104
104
  "@pnpm/testing.command-defaults": "1100.0.11",
@@ -108,7 +108,7 @@
108
108
  "@types/libnpmpublish": "^11.2.0",
109
109
  "@types/npm-registry-fetch": "^8.0.9",
110
110
  "@types/proxyquire": "^1.3.31",
111
- "@types/ramda": "0.31.1",
111
+ "@types/ramda": "0.32.0",
112
112
  "@types/semver": "7.7.1",
113
113
  "@types/tar-stream": "^3.1.4",
114
114
  "@types/validate-npm-package-name": "^4.0.2",
@@ -116,7 +116,7 @@
116
116
  "cross-spawn": "^7.0.6",
117
117
  "is-windows": "^1.0.2",
118
118
  "load-json-file": "^7.0.1",
119
- "tar": "^7.5.15",
119
+ "tar": "^7.5.22",
120
120
  "undici": "^7.27.2",
121
121
  "write-yaml-file": "^6.0.0"
122
122
  },