@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
@@ -0,0 +1,89 @@
1
+ import { PnpmError } from '@pnpm/error';
2
+ import type { ExportedManifest } from '@pnpm/releasing.exportable-manifest';
3
+ import type { PublishOptions } from 'libnpmpublish';
4
+ import { SHARED_CONTEXT } from './utils/shared-context.js';
5
+ export interface OtpWebAuthFetchOptions {
6
+ method: 'GET';
7
+ retry?: {
8
+ factor?: number;
9
+ maxTimeout?: number;
10
+ minTimeout?: number;
11
+ randomize?: boolean;
12
+ retries?: number;
13
+ };
14
+ timeout?: number;
15
+ }
16
+ export interface OtpWebAuthFetchResponseHeaders {
17
+ get: (this: this, name: 'retry-after') => string | null;
18
+ }
19
+ export interface OtpWebAuthFetchResponse {
20
+ readonly headers: OtpWebAuthFetchResponseHeaders;
21
+ readonly json: (this: this) => Promise<unknown>;
22
+ readonly ok: boolean;
23
+ readonly status: number;
24
+ }
25
+ export interface OtpPublishResponse {
26
+ readonly ok: boolean;
27
+ readonly status: number;
28
+ readonly statusText: string;
29
+ readonly text: () => Promise<string>;
30
+ }
31
+ export interface OtpEnquirer {
32
+ prompt: (this: this, options: OtpEnquirerOptions) => Promise<OtpEnquirerResponse | undefined>;
33
+ }
34
+ export interface OtpEnquirerOptions {
35
+ message: string;
36
+ name: 'otp';
37
+ type: 'input';
38
+ }
39
+ export interface OtpEnquirerResponse {
40
+ otp?: string;
41
+ }
42
+ export type OtpPublishFn = (manifest: ExportedManifest, tarballData: Buffer, options: PublishOptions) => Promise<OtpPublishResponse>;
43
+ export interface OtpDate {
44
+ now: () => number;
45
+ }
46
+ export interface OtpContext {
47
+ Date: OtpDate;
48
+ setTimeout: (cb: () => void, ms: number) => void;
49
+ enquirer: OtpEnquirer;
50
+ fetch: (url: string, options: OtpWebAuthFetchOptions) => Promise<OtpWebAuthFetchResponse>;
51
+ globalInfo: (message: string) => void;
52
+ process: Record<'stdin' | 'stdout', {
53
+ isTTY?: boolean;
54
+ }>;
55
+ publish: OtpPublishFn;
56
+ }
57
+ export interface OtpParams {
58
+ context?: OtpContext;
59
+ manifest: ExportedManifest;
60
+ publishOptions: PublishOptions;
61
+ tarballData: Buffer;
62
+ }
63
+ export { SHARED_CONTEXT };
64
+ /**
65
+ * Publish a package, handling OTP challenges:
66
+ * - Web based authentication flow (authUrl/doneUrl in error body with doneUrl polling)
67
+ * - Classic OTP prompt (manual code entry)
68
+ *
69
+ * @throws {@link OtpWebAuthTimeoutError} if the webauth browser flow times out.
70
+ * @throws {@link OtpNonInteractiveError} if OTP is required but the terminal is not interactive.
71
+ * @throws {@link OtpSecondChallengeError} if the registry requests OTP a second time after one was submitted.
72
+ * @throws the original error if OTP handling is not applicable.
73
+ *
74
+ * @see https://github.com/npm/cli/blob/7d900c46/lib/utils/otplease.js for npm's implementation.
75
+ * @see https://github.com/npm/npm-profile/blob/main/lib/index.js for the webauth polling flow.
76
+ */
77
+ export declare function publishWithOtpHandling({ context: { Date, setTimeout, enquirer, fetch, globalInfo, process, publish }, manifest, publishOptions, tarballData }: OtpParams): Promise<OtpPublishResponse>;
78
+ export declare class OtpWebAuthTimeoutError extends PnpmError {
79
+ readonly endTime: number;
80
+ readonly startTime: number;
81
+ readonly timeout: number;
82
+ constructor(endTime: number, startTime: number, timeout: number);
83
+ }
84
+ export declare class OtpNonInteractiveError extends PnpmError {
85
+ constructor();
86
+ }
87
+ export declare class OtpSecondChallengeError extends PnpmError {
88
+ constructor();
89
+ }
@@ -0,0 +1,165 @@
1
+ import { PnpmError } from '@pnpm/error';
2
+ import qrcodeTerminal from 'qrcode-terminal';
3
+ import { SHARED_CONTEXT } from './utils/shared-context.js';
4
+ export { SHARED_CONTEXT };
5
+ const isOtpError = (error) => error != null &&
6
+ typeof error === 'object' &&
7
+ 'code' in error &&
8
+ error.code === 'EOTP';
9
+ /**
10
+ * Publish a package, handling OTP challenges:
11
+ * - Web based authentication flow (authUrl/doneUrl in error body with doneUrl polling)
12
+ * - Classic OTP prompt (manual code entry)
13
+ *
14
+ * @throws {@link OtpWebAuthTimeoutError} if the webauth browser flow times out.
15
+ * @throws {@link OtpNonInteractiveError} if OTP is required but the terminal is not interactive.
16
+ * @throws {@link OtpSecondChallengeError} if the registry requests OTP a second time after one was submitted.
17
+ * @throws the original error if OTP handling is not applicable.
18
+ *
19
+ * @see https://github.com/npm/cli/blob/7d900c46/lib/utils/otplease.js for npm's implementation.
20
+ * @see https://github.com/npm/npm-profile/blob/main/lib/index.js for the webauth polling flow.
21
+ */
22
+ export async function publishWithOtpHandling({ context: { Date, setTimeout, enquirer, fetch, globalInfo, process, publish, } = SHARED_CONTEXT, manifest, publishOptions, tarballData, }) {
23
+ let response;
24
+ try {
25
+ response = await publish(manifest, tarballData, publishOptions);
26
+ }
27
+ catch (error) {
28
+ if (!isOtpError(error))
29
+ throw error;
30
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
31
+ throw new OtpNonInteractiveError();
32
+ }
33
+ const fetchOptions = {
34
+ method: 'GET',
35
+ retry: {
36
+ factor: publishOptions.fetchRetryFactor,
37
+ maxTimeout: publishOptions.fetchRetryMaxtimeout,
38
+ minTimeout: publishOptions.fetchRetryMintimeout,
39
+ retries: publishOptions.fetchRetries,
40
+ },
41
+ timeout: publishOptions.timeout,
42
+ };
43
+ let otp;
44
+ if (error.body?.authUrl && error.body?.doneUrl) {
45
+ otp = await webAuthOtp(error.body.authUrl, error.body.doneUrl, { Date, setTimeout, fetch, globalInfo }, fetchOptions);
46
+ }
47
+ else {
48
+ const enquirerResponse = await enquirer.prompt({
49
+ message: 'This operation requires a one-time password.\nEnter OTP:',
50
+ name: 'otp',
51
+ type: 'input',
52
+ });
53
+ // Use || (not ??) so that empty-string input is treated as "no OTP provided"
54
+ otp = enquirerResponse?.otp || undefined;
55
+ }
56
+ if (otp != null) {
57
+ try {
58
+ return await publish(manifest, tarballData, { ...publishOptions, otp });
59
+ }
60
+ catch (retryError) {
61
+ if (isOtpError(retryError)) {
62
+ throw new OtpSecondChallengeError();
63
+ }
64
+ throw retryError;
65
+ }
66
+ }
67
+ throw error;
68
+ }
69
+ return response;
70
+ }
71
+ async function webAuthOtp(authUrl, doneUrl, { Date, setTimeout, fetch, globalInfo }, fetchOptions) {
72
+ const qrCode = generateQrCode(authUrl);
73
+ globalInfo(`Authenticate your account at:\n${authUrl}\n\n${qrCode}`);
74
+ const startTime = Date.now();
75
+ const timeout = 5 * 60 * 1000; // 5 minutes
76
+ const pollIntervalMs = 1000;
77
+ while (true) {
78
+ const now = Date.now();
79
+ if (now - startTime > timeout) {
80
+ throw new OtpWebAuthTimeoutError(now, startTime, timeout);
81
+ }
82
+ // eslint-disable-next-line no-await-in-loop
83
+ await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
84
+ let response;
85
+ try {
86
+ // eslint-disable-next-line no-await-in-loop
87
+ response = await fetch(doneUrl, fetchOptions);
88
+ }
89
+ catch {
90
+ continue;
91
+ }
92
+ if (!response.ok)
93
+ continue;
94
+ if (response.status === 202) {
95
+ // Registry is still waiting for authentication.
96
+ // Respect Retry-After header if present by waiting the additional time
97
+ // beyond the default poll interval already elapsed above, but do not
98
+ // exceed the overall timeout.
99
+ const retryAfterSeconds = Number(response.headers.get('retry-after'));
100
+ if (Number.isFinite(retryAfterSeconds)) {
101
+ const additionalMs = retryAfterSeconds * 1000 - pollIntervalMs;
102
+ if (additionalMs > 0) {
103
+ const nowAfterPoll = Date.now();
104
+ const remainingMs = timeout - (nowAfterPoll - startTime);
105
+ if (remainingMs <= 0) {
106
+ throw new OtpWebAuthTimeoutError(nowAfterPoll, startTime, timeout);
107
+ }
108
+ const sleepMs = Math.min(additionalMs, remainingMs);
109
+ // eslint-disable-next-line no-await-in-loop
110
+ await new Promise(resolve => setTimeout(resolve, sleepMs));
111
+ }
112
+ }
113
+ continue;
114
+ }
115
+ let body;
116
+ try {
117
+ // eslint-disable-next-line no-await-in-loop
118
+ body = await response.json();
119
+ }
120
+ catch {
121
+ continue;
122
+ }
123
+ if (body.token) {
124
+ return body.token;
125
+ }
126
+ }
127
+ }
128
+ function generateQrCode(text) {
129
+ let qrCode;
130
+ qrcodeTerminal.generate(text, { small: true }, (code) => {
131
+ qrCode = code;
132
+ });
133
+ if (qrCode != null)
134
+ return qrCode;
135
+ /* istanbul ignore next */
136
+ throw new Error('we were expecting qrcode-terminal to be fully synchronous, but it fails to execute the callback');
137
+ }
138
+ export class OtpWebAuthTimeoutError extends PnpmError {
139
+ endTime;
140
+ startTime;
141
+ timeout;
142
+ constructor(endTime, startTime, timeout) {
143
+ super('WEBAUTH_TIMEOUT', 'Web-based authentication timed out before it could be completed', {
144
+ hint: 'Re-run this command and complete the authentication step in your browser before the time limit is reached',
145
+ });
146
+ this.endTime = endTime;
147
+ this.startTime = startTime;
148
+ this.timeout = timeout;
149
+ }
150
+ }
151
+ export class OtpNonInteractiveError extends PnpmError {
152
+ constructor() {
153
+ super('OTP_NON_INTERACTIVE', 'The registry requires additional authentication, but pnpm is not running in an interactive terminal', {
154
+ hint: 'Re-run this command in an interactive terminal to complete authentication, or provide the --otp option if you are using a classic one-time password (OTP)',
155
+ });
156
+ }
157
+ }
158
+ export class OtpSecondChallengeError extends PnpmError {
159
+ constructor() {
160
+ super('OTP_SECOND_CHALLENGE', 'The registry requested a one-time password (OTP) a second time after one was already provided', {
161
+ hint: 'This is unexpected behavior from the registry. Try the command again later and, if the issue persists, verify that your registry supports OTP-based authentication or contact the registry administrator.',
162
+ });
163
+ }
164
+ }
165
+ //# sourceMappingURL=otp.js.map
@@ -0,0 +1,7 @@
1
+ declare const ENV_KEY = "PNPM_CONFIG_OTP";
2
+ type EnvBase = Partial<Readonly<Record<string, string>>> & Partial<Readonly<Record<typeof ENV_KEY, string>>>;
3
+ interface OptionsBase {
4
+ readonly otp?: string;
5
+ }
6
+ export declare const optionsWithOtpEnv: <Options extends OptionsBase>(opts: Options, { [ENV_KEY]: otp }: EnvBase) => Options;
7
+ export {};
@@ -0,0 +1,5 @@
1
+ const ENV_KEY = 'PNPM_CONFIG_OTP';
2
+ export const optionsWithOtpEnv = (opts, { [ENV_KEY]: otp }) => Boolean(opts.otp) || !otp // empty string is considered "not defined" here
3
+ ? opts
4
+ : { ...opts, otp };
5
+ //# sourceMappingURL=otpEnv.js.map
@@ -0,0 +1,32 @@
1
+ import { type Config, type UniversalOptions } from '@pnpm/config.reader';
2
+ import { type ExportedManifest } from '@pnpm/releasing.exportable-manifest';
3
+ export declare function rcOptionsTypes(): Record<string, unknown>;
4
+ export declare function cliOptionsTypes(): Record<string, unknown>;
5
+ export declare const commandNames: string[];
6
+ export declare function help(): string;
7
+ export type PackOptions = Pick<UniversalOptions, 'dir'> & Pick<Config, 'catalogs' | 'ignoreScripts' | 'rawConfig' | 'embedReadme' | 'packGzipLevel' | 'nodeLinker'> & Partial<Pick<Config, 'extraBinPaths' | 'extraEnv' | 'hooks' | 'recursive' | 'selectedProjectsGraph' | 'workspaceConcurrency' | 'workspaceDir'>> & {
8
+ argv: {
9
+ original: string[];
10
+ };
11
+ dryRun?: boolean;
12
+ engineStrict?: boolean;
13
+ packDestination?: string;
14
+ out?: string;
15
+ json?: boolean;
16
+ unicode?: boolean;
17
+ };
18
+ export interface PackResultJson {
19
+ name: string;
20
+ version: string;
21
+ filename: string;
22
+ files: Array<{
23
+ path: string;
24
+ }>;
25
+ }
26
+ export declare function handler(opts: PackOptions): Promise<string>;
27
+ export declare function api(opts: PackOptions): Promise<PackResult>;
28
+ export interface PackResult {
29
+ publishedManifest: ExportedManifest;
30
+ contents: string[];
31
+ tarballPath: string;
32
+ }
@@ -0,0 +1,303 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createGzip } from 'node:zlib';
4
+ import { getBinsFromPackageManifest } from '@pnpm/bins.resolver';
5
+ import { FILTERING } from '@pnpm/cli.common-cli-options-help';
6
+ import { readProjectManifest } from '@pnpm/cli.utils';
7
+ import { getDefaultWorkspaceConcurrency, getWorkspaceConcurrency, types as allTypes } from '@pnpm/config.reader';
8
+ import { PnpmError } from '@pnpm/error';
9
+ import { packlist } from '@pnpm/fs.packlist';
10
+ import { logger } from '@pnpm/logger';
11
+ import { createExportableManifest } from '@pnpm/releasing.exportable-manifest';
12
+ import { sortProjects } from '@pnpm/workspace.projects-sorter';
13
+ import chalk from 'chalk';
14
+ import pLimit from 'p-limit';
15
+ import { pick } from 'ramda';
16
+ import { realpathMissing } from 'realpath-missing';
17
+ import { renderHelp } from 'render-help';
18
+ import tar from 'tar-stream';
19
+ import { glob } from 'tinyglobby';
20
+ import validateNpmPackageName from 'validate-npm-package-name';
21
+ import { runScriptsIfPresent } from './publish.js';
22
+ const LICENSE_GLOB = 'LICEN{S,C}E{,.*}'; // cspell:disable-line
23
+ export function rcOptionsTypes() {
24
+ return {
25
+ ...cliOptionsTypes(),
26
+ ...pick([
27
+ 'npm-path',
28
+ ], allTypes),
29
+ };
30
+ }
31
+ export function cliOptionsTypes() {
32
+ return {
33
+ out: String,
34
+ recursive: Boolean,
35
+ ...pick([
36
+ 'dry-run',
37
+ 'pack-destination',
38
+ 'pack-gzip-level',
39
+ 'json',
40
+ 'workspace-concurrency',
41
+ ], allTypes),
42
+ };
43
+ }
44
+ export const commandNames = ['pack'];
45
+ export function help() {
46
+ return renderHelp({
47
+ description: 'Create a tarball from a package',
48
+ usages: ['pnpm pack'],
49
+ descriptionLists: [
50
+ {
51
+ title: 'Options',
52
+ list: [
53
+ {
54
+ description: 'Does everything `pnpm pack` would do except actually writing the tarball to disk.',
55
+ name: '--dry-run',
56
+ },
57
+ {
58
+ description: 'Directory in which `pnpm pack` will save tarballs. The default is the current working directory.',
59
+ name: '--pack-destination <dir>',
60
+ },
61
+ {
62
+ description: 'Prints the packed tarball and contents in the json format.',
63
+ name: '--json',
64
+ },
65
+ {
66
+ description: 'Customizes the output path for the tarball. Use `%s` and `%v` to include the package name and version, e.g., `%s.tgz` or `some-dir/%s-%v.tgz`. By default, the tarball is saved in the current working directory with the name `<package-name>-<version>.tgz`.',
67
+ name: '--out <path>',
68
+ },
69
+ {
70
+ description: 'Pack all packages from the workspace',
71
+ name: '--recursive',
72
+ shortAlias: '-r',
73
+ },
74
+ {
75
+ description: `Set the maximum number of concurrency. Default is ${getDefaultWorkspaceConcurrency()}. For unlimited concurrency use Infinity.`,
76
+ name: '--workspace-concurrency <number>',
77
+ },
78
+ ],
79
+ },
80
+ FILTERING,
81
+ ],
82
+ });
83
+ }
84
+ export async function handler(opts) {
85
+ const packedPackages = [];
86
+ if (opts.recursive) {
87
+ const selectedProjectsGraph = opts.selectedProjectsGraph;
88
+ const pkgsToPack = [];
89
+ for (const { package: pkg } of Object.values(selectedProjectsGraph)) {
90
+ if (pkg.manifest.name && pkg.manifest.version) {
91
+ pkgsToPack.push(pkg);
92
+ }
93
+ }
94
+ const packedPkgDirs = new Set(pkgsToPack.map(({ rootDir }) => rootDir));
95
+ if (packedPkgDirs.size === 0) {
96
+ logger.info({
97
+ message: 'There are no packages that should be packed',
98
+ prefix: opts.dir,
99
+ });
100
+ }
101
+ const chunks = sortProjects(selectedProjectsGraph);
102
+ const limitPack = pLimit(getWorkspaceConcurrency(opts.workspaceConcurrency));
103
+ const resolvedOpts = { ...opts };
104
+ if (opts.out) {
105
+ resolvedOpts.out = path.resolve(opts.dir, opts.out);
106
+ }
107
+ else if (opts.packDestination) {
108
+ resolvedOpts.packDestination = path.resolve(opts.dir, opts.packDestination);
109
+ }
110
+ else {
111
+ resolvedOpts.packDestination = path.resolve(opts.dir);
112
+ }
113
+ for (const chunk of chunks) {
114
+ // eslint-disable-next-line no-await-in-loop
115
+ await Promise.all(chunk.map(pkgDir => limitPack(async () => {
116
+ if (!packedPkgDirs.has(pkgDir))
117
+ return;
118
+ const pkg = selectedProjectsGraph[pkgDir].package;
119
+ const packResult = await api({
120
+ ...resolvedOpts,
121
+ dir: pkg.rootDir,
122
+ });
123
+ packedPackages.push(toPackResultJson(packResult));
124
+ })));
125
+ }
126
+ }
127
+ else {
128
+ const packResult = await api(opts);
129
+ packedPackages.push(toPackResultJson(packResult));
130
+ }
131
+ if (opts.json) {
132
+ return JSON.stringify(packedPackages.length > 1 ? packedPackages : packedPackages[0], null, 2);
133
+ }
134
+ return packedPackages.map(({ name, version, filename, files }) => `${opts.unicode ? '📦 ' : 'package:'} ${name}@${version}
135
+ ${chalk.blueBright('Tarball Contents')}
136
+ ${files.map(({ path }) => path).join('\n')}
137
+ ${chalk.blueBright('Tarball Details')}
138
+ ${filename}`).join('\n\n');
139
+ }
140
+ export async function api(opts) {
141
+ const { manifest: entryManifest, fileName: manifestFileName } = await readProjectManifest(opts.dir, opts);
142
+ preventBundledDependenciesWithoutHoistedNodeLinker(opts.nodeLinker, entryManifest);
143
+ const _runScriptsIfPresent = runScriptsIfPresent.bind(null, {
144
+ depPath: opts.dir,
145
+ extraBinPaths: opts.extraBinPaths,
146
+ extraEnv: opts.extraEnv,
147
+ pkgRoot: opts.dir,
148
+ rawConfig: opts.rawConfig,
149
+ rootModulesDir: await realpathMissing(path.join(opts.dir, 'node_modules')),
150
+ stdio: 'inherit',
151
+ unsafePerm: true, // when running scripts explicitly, assume that they're trusted.
152
+ });
153
+ if (!opts.ignoreScripts) {
154
+ await _runScriptsIfPresent([
155
+ 'prepack',
156
+ 'prepare',
157
+ ], entryManifest);
158
+ }
159
+ const dir = entryManifest.publishConfig?.directory
160
+ ? path.join(opts.dir, entryManifest.publishConfig.directory)
161
+ : opts.dir;
162
+ // always read the latest manifest, as "prepack" or "prepare" script may modify package manifest.
163
+ const { manifest } = await readProjectManifest(dir, opts);
164
+ preventBundledDependenciesWithoutHoistedNodeLinker(opts.nodeLinker, manifest);
165
+ if (!manifest.name) {
166
+ throw new PnpmError('PACKAGE_NAME_NOT_FOUND', `Package name is not defined in the ${manifestFileName}.`);
167
+ }
168
+ if (!validateNpmPackageName(manifest.name).validForOldPackages) {
169
+ throw new PnpmError('INVALID_PACKAGE_NAME', `Invalid package name "${manifest.name}".`);
170
+ }
171
+ if (!manifest.version) {
172
+ throw new PnpmError('PACKAGE_VERSION_NOT_FOUND', `Package version is not defined in the ${manifestFileName}.`);
173
+ }
174
+ let tarballName;
175
+ let packDestination;
176
+ const normalizedName = manifest.name.replace('@', '').replace('/', '-');
177
+ if (opts.out) {
178
+ if (opts.packDestination) {
179
+ throw new PnpmError('INVALID_OPTION', 'Cannot use --pack-destination and --out together');
180
+ }
181
+ const preparedOut = opts.out.replaceAll('%s', normalizedName).replaceAll('%v', manifest.version);
182
+ const parsedOut = path.parse(preparedOut);
183
+ packDestination = parsedOut.dir ? parsedOut.dir : opts.packDestination;
184
+ tarballName = parsedOut.base;
185
+ }
186
+ else {
187
+ tarballName = `${normalizedName}-${manifest.version}.tgz`;
188
+ packDestination = opts.packDestination;
189
+ }
190
+ const publishManifest = await createPublishManifest({
191
+ projectDir: dir,
192
+ modulesDir: path.join(opts.dir, 'node_modules'),
193
+ manifest,
194
+ embedReadme: opts.embedReadme,
195
+ catalogs: opts.catalogs ?? {},
196
+ hooks: opts.hooks,
197
+ });
198
+ const files = await packlist(dir, {
199
+ manifest: publishManifest,
200
+ });
201
+ const filesMap = Object.fromEntries(files.map((file) => [`package/${file}`, path.join(dir, file)]));
202
+ // cspell:disable-next-line
203
+ if (opts.workspaceDir != null && dir !== opts.workspaceDir && !files.some((file) => /LICEN[CS]E(?:\..+)?/i.test(file))) {
204
+ const licenses = await glob([LICENSE_GLOB], { cwd: opts.workspaceDir, expandDirectories: false });
205
+ for (const license of licenses) {
206
+ filesMap[`package/${license}`] = path.join(opts.workspaceDir, license);
207
+ }
208
+ }
209
+ const destDir = packDestination
210
+ ? (path.isAbsolute(packDestination) ? packDestination : path.join(dir, packDestination ?? '.'))
211
+ : dir;
212
+ if (!opts.dryRun) {
213
+ await fs.promises.mkdir(destDir, { recursive: true });
214
+ await packPkg({
215
+ destFile: path.join(destDir, tarballName),
216
+ filesMap,
217
+ modulesDir: path.join(opts.dir, 'node_modules'),
218
+ packGzipLevel: opts.packGzipLevel,
219
+ manifest: publishManifest,
220
+ bins: [
221
+ ...(await getBinsFromPackageManifest(publishManifest, dir)).map(({ path }) => path),
222
+ ...(manifest.publishConfig?.executableFiles ?? [])
223
+ .map((executableFile) => path.join(dir, executableFile)),
224
+ ],
225
+ });
226
+ if (!opts.ignoreScripts) {
227
+ await _runScriptsIfPresent(['postpack'], entryManifest);
228
+ }
229
+ }
230
+ let packedTarballPath;
231
+ if (opts.dir !== destDir) {
232
+ packedTarballPath = path.join(destDir, tarballName);
233
+ }
234
+ else {
235
+ packedTarballPath = path.relative(opts.dir, path.join(dir, tarballName));
236
+ }
237
+ const packedContents = files.sort((a, b) => a.localeCompare(b, 'en'));
238
+ return {
239
+ publishedManifest: publishManifest,
240
+ contents: packedContents,
241
+ tarballPath: packedTarballPath,
242
+ };
243
+ }
244
+ function preventBundledDependenciesWithoutHoistedNodeLinker(nodeLinker, manifest) {
245
+ if (nodeLinker === 'hoisted')
246
+ return;
247
+ for (const key of ['bundledDependencies', 'bundleDependencies']) {
248
+ const bundledDependencies = manifest[key];
249
+ if (bundledDependencies) {
250
+ throw new PnpmError('BUNDLED_DEPENDENCIES_WITHOUT_HOISTED', `${key} does not work with "nodeLinker: ${nodeLinker}"`, {
251
+ hint: `Add "nodeLinker: hoisted" to pnpm-workspace.yaml or delete ${key} from the root package.json to resolve this error`,
252
+ });
253
+ }
254
+ }
255
+ }
256
+ async function readReadmeFile(projectDir) {
257
+ const files = await fs.promises.readdir(projectDir);
258
+ const readmePath = files.find(name => /readme\.md$/i.test(name));
259
+ const readmeFile = readmePath ? await fs.promises.readFile(path.join(projectDir, readmePath), 'utf8') : undefined;
260
+ return readmeFile;
261
+ }
262
+ async function packPkg(opts) {
263
+ const { destFile, filesMap, bins, manifest, } = opts;
264
+ const mtime = new Date('1985-10-26T08:15:00.000Z');
265
+ const pack = tar.pack();
266
+ await Promise.all(Object.entries(filesMap).map(async ([name, source]) => {
267
+ const isExecutable = bins.some((bin) => path.relative(bin, source) === '');
268
+ const mode = isExecutable ? 0o755 : 0o644;
269
+ if (/^package\/package\.(?:json|json5|yaml)$/.test(name)) {
270
+ pack.entry({ mode, mtime, name: 'package/package.json' }, JSON.stringify(manifest, null, 2));
271
+ return;
272
+ }
273
+ pack.entry({ mode, mtime, name }, fs.readFileSync(source));
274
+ }));
275
+ const tarball = fs.createWriteStream(destFile);
276
+ pack.pipe(createGzip({ level: opts.packGzipLevel })).pipe(tarball);
277
+ pack.finalize();
278
+ return new Promise((resolve, reject) => {
279
+ tarball.on('close', () => {
280
+ resolve();
281
+ }).on('error', reject);
282
+ });
283
+ }
284
+ async function createPublishManifest(opts) {
285
+ const { projectDir, embedReadme, modulesDir, manifest, catalogs, hooks } = opts;
286
+ const readmeFile = embedReadme ? await readReadmeFile(projectDir) : undefined;
287
+ return createExportableManifest(projectDir, manifest, {
288
+ catalogs,
289
+ hooks,
290
+ readmeFile,
291
+ modulesDir,
292
+ });
293
+ }
294
+ function toPackResultJson(packResult) {
295
+ const { publishedManifest, contents, tarballPath } = packResult;
296
+ return {
297
+ name: publishedManifest.name,
298
+ version: publishedManifest.version,
299
+ filename: tarballPath,
300
+ files: contents.map((file) => ({ path: file })),
301
+ };
302
+ }
303
+ //# sourceMappingURL=pack.js.map
@@ -0,0 +1,31 @@
1
+ import { type Config } from '@pnpm/config.reader';
2
+ import { type RunLifecycleHookOptions } from '@pnpm/exec.lifecycle';
3
+ import type { ProjectManifest } from '@pnpm/types';
4
+ import { type PublishRecursiveOpts } from './recursivePublish.js';
5
+ export declare function rcOptionsTypes(): Record<string, unknown>;
6
+ export declare function cliOptionsTypes(): Record<string, unknown>;
7
+ export declare const commandNames: string[];
8
+ export declare function help(): string;
9
+ export declare function handler(opts: Omit<PublishRecursiveOpts, 'workspaceDir'> & {
10
+ argv: {
11
+ original: string[];
12
+ };
13
+ engineStrict?: boolean;
14
+ recursive?: boolean;
15
+ workspaceDir?: string;
16
+ } & Pick<Config, 'allProjects' | 'bin' | 'gitChecks' | 'ignoreScripts' | 'pnpmHomeDir' | 'publishBranch' | 'embedReadme'>, params: string[]): Promise<{
17
+ exitCode?: number;
18
+ } | undefined>;
19
+ export interface PublishResult {
20
+ exitCode?: number;
21
+ manifest?: ProjectManifest;
22
+ }
23
+ export declare function publish(opts: Omit<PublishRecursiveOpts, 'workspaceDir'> & {
24
+ argv: {
25
+ original: string[];
26
+ };
27
+ engineStrict?: boolean;
28
+ recursive?: boolean;
29
+ workspaceDir?: string;
30
+ } & Pick<Config, 'allProjects' | 'bin' | 'gitChecks' | 'ignoreScripts' | 'pnpmHomeDir' | 'publishBranch' | 'embedReadme' | 'packGzipLevel'>, params: string[]): Promise<PublishResult>;
31
+ export declare function runScriptsIfPresent(opts: RunLifecycleHookOptions, scriptNames: string[], manifest: ProjectManifest): Promise<void>;