@jsdevtools/npm-publish 1.4.2 → 2.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 (90) hide show
  1. package/README.md +161 -147
  2. package/bin/npm-publish.js +10 -2
  3. package/lib/action/core.d.ts +45 -0
  4. package/lib/action/core.js +59 -0
  5. package/lib/action/core.js.map +1 -0
  6. package/lib/action/main.js +54 -0
  7. package/lib/action/main.js.map +1 -0
  8. package/lib/cli/index.d.ts +4 -2
  9. package/lib/cli/index.js +57 -53
  10. package/lib/cli/index.js.map +1 -1
  11. package/lib/cli/parse-cli-arguments.d.ts +17 -0
  12. package/lib/cli/parse-cli-arguments.js +39 -0
  13. package/lib/cli/parse-cli-arguments.js.map +1 -0
  14. package/lib/compare-versions.d.ts +20 -0
  15. package/lib/compare-versions.js +36 -0
  16. package/lib/compare-versions.js.map +1 -0
  17. package/lib/errors.d.ts +36 -0
  18. package/lib/errors.js +114 -0
  19. package/lib/errors.js.map +1 -0
  20. package/lib/format-publish-result.d.ts +12 -0
  21. package/lib/format-publish-result.js +36 -0
  22. package/lib/format-publish-result.js.map +1 -0
  23. package/lib/index.d.ts +4 -5
  24. package/lib/index.js +11 -10
  25. package/lib/index.js.map +1 -1
  26. package/lib/normalize-options.d.ts +26 -1
  27. package/lib/normalize-options.js +86 -13
  28. package/lib/normalize-options.js.map +1 -1
  29. package/lib/npm/call-npm-cli.d.ts +16 -0
  30. package/lib/npm/call-npm-cli.js +91 -0
  31. package/lib/npm/call-npm-cli.js.map +1 -0
  32. package/lib/npm/get-publish-arguments.d.ts +9 -0
  33. package/lib/npm/get-publish-arguments.js +29 -0
  34. package/lib/npm/get-publish-arguments.js.map +1 -0
  35. package/lib/npm/index.d.ts +29 -0
  36. package/lib/npm/index.js +41 -0
  37. package/lib/npm/index.js.map +1 -0
  38. package/lib/npm/use-npm-environment.d.ts +13 -0
  39. package/lib/npm/use-npm-environment.js +42 -0
  40. package/lib/npm/use-npm-environment.js.map +1 -0
  41. package/lib/npm-publish.d.ts +7 -4
  42. package/lib/npm-publish.js +29 -25
  43. package/lib/npm-publish.js.map +1 -1
  44. package/lib/options.d.ts +58 -46
  45. package/lib/options.js +5 -0
  46. package/lib/options.js.map +1 -1
  47. package/lib/read-manifest.d.ts +25 -1
  48. package/lib/read-manifest.js +119 -22
  49. package/lib/read-manifest.js.map +1 -1
  50. package/lib/results.d.ts +26 -28
  51. package/package.json +43 -35
  52. package/src/action/core.ts +91 -0
  53. package/src/action/main.ts +31 -0
  54. package/src/cli/index.ts +69 -0
  55. package/src/cli/parse-cli-arguments.ts +48 -0
  56. package/src/compare-versions.ts +52 -0
  57. package/src/errors.ts +130 -0
  58. package/src/format-publish-result.ts +40 -0
  59. package/src/index.ts +7 -0
  60. package/src/normalize-options.ts +119 -0
  61. package/src/npm/call-npm-cli.ts +98 -0
  62. package/src/npm/get-publish-arguments.ts +34 -0
  63. package/src/npm/index.ts +64 -0
  64. package/src/npm/use-npm-environment.ts +51 -0
  65. package/src/npm-publish.ts +47 -0
  66. package/src/options.ts +96 -0
  67. package/src/read-manifest.ts +143 -0
  68. package/src/results.ts +45 -0
  69. package/CHANGELOG.md +0 -49
  70. package/lib/action/index.js +0 -67
  71. package/lib/action/index.js.map +0 -1
  72. package/lib/cli/exit-code.d.ts +0 -1
  73. package/lib/cli/exit-code.js +0 -16
  74. package/lib/cli/exit-code.js.map +0 -1
  75. package/lib/cli/help.d.ts +0 -1
  76. package/lib/cli/help.js +0 -28
  77. package/lib/cli/help.js.map +0 -1
  78. package/lib/cli/parse-args.d.ts +0 -1
  79. package/lib/cli/parse-args.js +0 -58
  80. package/lib/cli/parse-args.js.map +0 -1
  81. package/lib/npm-config.d.ts +0 -1
  82. package/lib/npm-config.js +0 -85
  83. package/lib/npm-config.js.map +0 -1
  84. package/lib/npm-env.d.ts +0 -6
  85. package/lib/npm-env.js +0 -24
  86. package/lib/npm-env.js.map +0 -1
  87. package/lib/npm.d.ts +0 -1
  88. package/lib/npm.js +0 -95
  89. package/lib/npm.js.map +0 -1
  90. /package/lib/action/{index.d.ts → main.d.ts} +0 -0
@@ -0,0 +1,69 @@
1
+ import { npmPublish, type Logger } from "../index.js";
2
+ import { parseCliArguments } from "./parse-cli-arguments.js";
3
+
4
+ export const USAGE = `
5
+ Usage:
6
+
7
+ npm-publish <options> [package]
8
+
9
+ Arguments:
10
+
11
+ package The path to the package to publish.
12
+ May be a directory, package.json, or .tgz file.
13
+ Defaults to the package in the current directory.
14
+
15
+ Options:
16
+
17
+ --token <token> (Required) npm authentication token.
18
+
19
+ --registry <url> Registry to read from and write to.
20
+ Defaults to "https://registry.npmjs.org/".
21
+
22
+ --tag <tag> The distribution tag to check against and publish to.
23
+ Defaults to "latest".
24
+
25
+ --access <access> Package access, may be "public" or "restricted".
26
+ See documentation for details.
27
+
28
+ --strategy <strategy> Publish strategy, may be "all" or "upgrade".
29
+ Defaults to "all", see documentation for details.
30
+
31
+ --dry-run Do not actually publish anything.
32
+ --quiet Only print errors.
33
+ --debug Print debug logs.
34
+
35
+ -v, --version Print the version number.
36
+ -h, --help Show usage text.
37
+
38
+ Examples:
39
+
40
+ $ npm-publish --token abc123 ./my-package
41
+ `;
42
+
43
+ /**
44
+ * The main entry point of the CLI
45
+ *
46
+ * @param argv - The list of argument strings passed to the program.
47
+ * @param version - The version of this program.
48
+ */
49
+ export async function main(argv: string[], version: string): Promise<void> {
50
+ const cliArguments = parseCliArguments(argv);
51
+
52
+ if (cliArguments.help) {
53
+ console.info(USAGE);
54
+ return;
55
+ }
56
+
57
+ if (cliArguments.version) {
58
+ console.info(version);
59
+ return;
60
+ }
61
+
62
+ const logger: Logger = {
63
+ error: console.error,
64
+ info: cliArguments.quiet === false ? console.info : undefined,
65
+ debug: cliArguments.debug === true ? console.debug : undefined,
66
+ };
67
+
68
+ await npmPublish({ ...cliArguments.options, logger });
69
+ }
@@ -0,0 +1,48 @@
1
+ /** Wrapper module for command-line-args */
2
+
3
+ import commandLineArgs from "command-line-args";
4
+ import type { Options } from "../options.js";
5
+
6
+ const ARGUMENTS_OPTIONS = [
7
+ { name: "package", type: String, defaultOption: true },
8
+ { name: "token", type: String },
9
+ { name: "registry", type: String },
10
+ { name: "tag", type: String },
11
+ { name: "access", type: String },
12
+ { name: "strategy", type: String },
13
+ { name: "dry-run", type: Boolean },
14
+ { name: "quiet", type: Boolean },
15
+ { name: "debug", type: Boolean },
16
+ { name: "version", alias: "v", type: Boolean },
17
+ { name: "help", alias: "h", type: Boolean },
18
+ ];
19
+
20
+ /** The parsed command-line arguments */
21
+ export interface ParsedArguments {
22
+ help?: boolean;
23
+ version?: boolean;
24
+ quiet?: boolean;
25
+ debug?: boolean;
26
+ options: Options;
27
+ }
28
+
29
+ /**
30
+ * Parse the given command-line arguments.
31
+ *
32
+ * @param argv The list of argument strings passed to the program.
33
+ * @returns A parsed object of options.
34
+ */
35
+ export function parseCliArguments(argv: string[]): ParsedArguments {
36
+ const { help, version, quiet, debug, ...options } = commandLineArgs(
37
+ ARGUMENTS_OPTIONS,
38
+ { argv, camelCase: true }
39
+ );
40
+
41
+ return {
42
+ help: Boolean(help),
43
+ version: Boolean(version),
44
+ quiet: Boolean(quiet),
45
+ debug: Boolean(debug),
46
+ options: options as Options,
47
+ };
48
+ }
@@ -0,0 +1,52 @@
1
+ import {
2
+ valid as semverValid,
3
+ gt as semverGreaterThan,
4
+ diff as semverDifference,
5
+ type ReleaseType as SemverReleaseType,
6
+ } from "semver";
7
+
8
+ import { STRATEGY_ALL } from "./options.js";
9
+ import type { NormalizedOptions } from "./normalize-options.js";
10
+ import type { PublishedVersions } from "./npm/index.js";
11
+
12
+ export type ReleaseType = SemverReleaseType | typeof INITIAL | typeof DIFFERENT;
13
+
14
+ export interface VersionComparison {
15
+ type: ReleaseType | undefined;
16
+ oldVersion: string | undefined;
17
+ }
18
+
19
+ const INITIAL = "initial";
20
+ const DIFFERENT = "different";
21
+
22
+ /**
23
+ * Compare previously published versions with the package's current version.
24
+ *
25
+ * @param version The current package version.
26
+ * @param publishedVersions The versions that have already been published.
27
+ * @param options Configuration options
28
+ * @returns The release type and previous version.
29
+ */
30
+ export function compareVersions(
31
+ version: string,
32
+ publishedVersions: PublishedVersions,
33
+ options: NormalizedOptions
34
+ ): VersionComparison {
35
+ const { versions: existingVersions, "dist-tags": tags } = publishedVersions;
36
+ const { strategy, tag: publishTag } = options;
37
+ const oldVersion = semverValid(tags[publishTag.value]) ?? undefined;
38
+ const isUnique = !existingVersions.includes(version);
39
+ let type: ReleaseType | undefined;
40
+
41
+ if (isUnique) {
42
+ if (!oldVersion) {
43
+ type = INITIAL;
44
+ } else if (semverGreaterThan(version, oldVersion)) {
45
+ type = semverDifference(version, oldVersion) ?? DIFFERENT;
46
+ } else if (strategy.value === STRATEGY_ALL) {
47
+ type = DIFFERENT;
48
+ }
49
+ }
50
+
51
+ return { type, oldVersion };
52
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,130 @@
1
+ import os from "node:os";
2
+
3
+ import {
4
+ ACCESS_PUBLIC,
5
+ ACCESS_RESTRICTED,
6
+ STRATEGY_ALL,
7
+ STRATEGY_UPGRADE,
8
+ } from "./options.js";
9
+
10
+ export class InvalidPackageError extends TypeError {
11
+ public constructor(value: unknown) {
12
+ super(
13
+ `Package must be a directory, package.json, or .tgz file, got "${String(
14
+ value
15
+ )}"`
16
+ );
17
+ this.name = "PackageJsonReadError";
18
+ }
19
+ }
20
+
21
+ export class PackageJsonReadError extends Error {
22
+ public constructor(manifestPath: string, originalError: unknown) {
23
+ const message = [
24
+ `Could not read package.json at ${manifestPath}`,
25
+ originalError instanceof Error ? originalError.message : "",
26
+ ]
27
+ .filter(Boolean)
28
+ .join(os.EOL);
29
+
30
+ super(message);
31
+ this.name = "PackageJsonReadError";
32
+ }
33
+ }
34
+
35
+ export class PackageTarballReadError extends Error {
36
+ public constructor(tarballPath: string, originalError: unknown) {
37
+ const message = [
38
+ `Could not read package.json from ${tarballPath}`,
39
+ originalError instanceof Error ? originalError.message : "",
40
+ ]
41
+ .filter(Boolean)
42
+ .join(os.EOL);
43
+
44
+ super(message);
45
+ this.name = "PackageTarballReadError";
46
+ }
47
+ }
48
+
49
+ export class PackageJsonParseError extends SyntaxError {
50
+ public constructor(packageSpec: string, originalError: unknown) {
51
+ const message = [
52
+ `Invalid JSON, could not parse package.json for ${packageSpec}`,
53
+ originalError instanceof Error ? originalError.message : "",
54
+ ]
55
+ .filter(Boolean)
56
+ .join(os.EOL);
57
+
58
+ super(message);
59
+ this.name = "PackageJsonParseError";
60
+ }
61
+ }
62
+
63
+ export class InvalidPackageNameError extends TypeError {
64
+ public constructor(value: unknown) {
65
+ super(`Package name must be a string, got "${String(value)}"`);
66
+ this.name = "InvalidPackageNameError";
67
+ }
68
+ }
69
+
70
+ export class InvalidPackageVersionError extends TypeError {
71
+ public constructor(value: unknown) {
72
+ super(`Package version must be a string, got "${String(value)}"`);
73
+ this.name = "InvalidPackageVersionError";
74
+ }
75
+ }
76
+
77
+ export class InvalidPackagePublishConfigError extends TypeError {
78
+ public constructor(value: unknown) {
79
+ super(`Publish config must be an object, got "${String(value)}"`);
80
+ this.name = "InvalidPackagePublishConfigError";
81
+ }
82
+ }
83
+
84
+ export class InvalidRegistryUrlError extends TypeError {
85
+ public constructor(value: unknown) {
86
+ super(`Registry URL invalid, got "${String(value)}"`);
87
+ this.name = "InvalidRegistryUrlError";
88
+ }
89
+ }
90
+
91
+ export class InvalidTokenError extends TypeError {
92
+ public constructor() {
93
+ super("Token must be a non-empty string.");
94
+ this.name = "InvalidTokenError";
95
+ }
96
+ }
97
+
98
+ export class InvalidAccessError extends TypeError {
99
+ public constructor(value: unknown) {
100
+ super(
101
+ `Access must be "${ACCESS_PUBLIC}" or "${ACCESS_RESTRICTED}", got "${String(
102
+ value
103
+ )}".`
104
+ );
105
+ this.name = "InvalidAccessError";
106
+ }
107
+ }
108
+
109
+ export class InvalidStrategyError extends TypeError {
110
+ public constructor(value: unknown) {
111
+ super(
112
+ `Strategy must be "${STRATEGY_UPGRADE}" or "${STRATEGY_ALL}", got "${String(
113
+ value
114
+ )}".`
115
+ );
116
+ this.name = "InvalidStrategyError";
117
+ }
118
+ }
119
+
120
+ export class NpmCallError extends Error {
121
+ public constructor(command: string, exitCode: number, stderr: string) {
122
+ super(
123
+ [
124
+ `Call to "npm ${command}" exited with non-zero exit code ${exitCode}`,
125
+ stderr,
126
+ ].join(os.EOL)
127
+ );
128
+ this.name = "NpmCallError";
129
+ }
130
+ }
@@ -0,0 +1,40 @@
1
+ import os from "node:os";
2
+
3
+ import type { PublishResult } from "./npm/index.js";
4
+ import type { PackageManifest } from "./read-manifest.js";
5
+ import type { NormalizedOptions } from "./normalize-options.js";
6
+
7
+ /**
8
+ * Format publish results into a string.
9
+ *
10
+ * @param manifest Package manifest
11
+ * @param options Configuration options.
12
+ * @param results Results from running npm publish.
13
+ * @returns Formatted string.
14
+ */
15
+ export function formatPublishResult(
16
+ manifest: PackageManifest,
17
+ options: NormalizedOptions,
18
+ results?: PublishResult
19
+ ): string {
20
+ if (results === undefined) {
21
+ return `🙅‍♀️ ${manifest.name}@${manifest.version} publish skipped.`;
22
+ }
23
+
24
+ return [
25
+ `📦 ${results.id}${options.dryRun.value ? " (DRY RUN)" : ""}`,
26
+ "=== Contents ===",
27
+ ...results.files.map(({ path, size }) => `${formatSize(size)}\t${path}`),
28
+ ].join(os.EOL);
29
+ }
30
+
31
+ const formatSize = (size: number): string => {
32
+ if (size < 1000) {
33
+ return `${size} B`;
34
+ }
35
+ if (size < 1_000_000) {
36
+ return `${(size / 1000).toFixed(1)} kB`;
37
+ }
38
+
39
+ return `${(size / 1_000_000).toFixed(1)} MB`;
40
+ };
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ // Export the external type definitions as named exports
2
+ export * from "./options.js";
3
+ export * from "./results.js";
4
+ export * from "./errors.js";
5
+
6
+ // Export `npmPublish` as a named export and the default export
7
+ export { npmPublish } from "./npm-publish.js";
@@ -0,0 +1,119 @@
1
+ import os from "node:os";
2
+
3
+ import * as errors from "./errors.js";
4
+ import type { PackageManifest } from "./read-manifest.js";
5
+ import {
6
+ ACCESS_PUBLIC,
7
+ ACCESS_RESTRICTED,
8
+ STRATEGY_UPGRADE,
9
+ STRATEGY_ALL,
10
+ type Access,
11
+ type Strategy,
12
+ type Options,
13
+ type Logger,
14
+ } from "./options.js";
15
+
16
+ const DEFAULT_REGISTRY = "https://registry.npmjs.org/";
17
+ const DEFAULT_TAG = "latest";
18
+
19
+ /** Normalized and sanitized auth, publish, and runtime configurations. */
20
+ export interface NormalizedOptions {
21
+ registry: URL;
22
+ token: string;
23
+ tag: ConfigValue<string>;
24
+ access: ConfigValue<Access | undefined>;
25
+ dryRun: ConfigValue<boolean>;
26
+ strategy: ConfigValue<Strategy>;
27
+ logger: Logger | undefined;
28
+ temporaryDirectory: string;
29
+ }
30
+
31
+ /** A config value, and whether that value differs from default. */
32
+ export interface ConfigValue<TValue> {
33
+ value: TValue;
34
+ isDefault: boolean;
35
+ }
36
+
37
+ /**
38
+ * Normalizes and sanitizes options, and fills-in any default values.
39
+ *
40
+ * @param options User-input options.
41
+ * @param manifest Package metadata from package.json.
42
+ * @returns Validated auth and publish configuration.
43
+ */
44
+ export function normalizeOptions(
45
+ options: Options,
46
+ manifest: PackageManifest
47
+ ): NormalizedOptions {
48
+ const defaultTag = manifest.publishConfig?.tag ?? DEFAULT_TAG;
49
+
50
+ const defaultRegistry = manifest.publishConfig?.registry ?? DEFAULT_REGISTRY;
51
+
52
+ const defaultAccess =
53
+ manifest.publishConfig?.access ??
54
+ (manifest.scope === undefined ? ACCESS_PUBLIC : undefined);
55
+
56
+ return {
57
+ token: validateToken(options.token),
58
+ registry: validateRegistry(options.registry ?? defaultRegistry),
59
+ tag: setValue(options.tag, defaultTag, validateTag),
60
+ access: setValue(options.access, defaultAccess, validateAccess),
61
+ dryRun: setValue(options.dryRun, false, validateDryRun),
62
+ strategy: setValue(options.strategy, STRATEGY_ALL, validateStrategy),
63
+ logger: options.logger,
64
+ temporaryDirectory: options.temporaryDirectory ?? os.tmpdir(),
65
+ };
66
+ }
67
+
68
+ const setValue = <TValue>(
69
+ value: unknown,
70
+ defaultValue: unknown,
71
+ validate: (value: unknown) => TValue
72
+ ): ConfigValue<TValue> => ({
73
+ value: validate(value ?? defaultValue),
74
+ isDefault: value === undefined,
75
+ });
76
+
77
+ const validateToken = (value: unknown): string => {
78
+ if (typeof value === "string" && value.length > 0) {
79
+ return value;
80
+ }
81
+
82
+ throw new errors.InvalidTokenError();
83
+ };
84
+
85
+ const validateRegistry = (value: unknown): URL => {
86
+ try {
87
+ return new URL(value as string | URL);
88
+ } catch {
89
+ throw new errors.InvalidRegistryUrlError(value);
90
+ }
91
+ };
92
+
93
+ const validateTag = (value: unknown): string => {
94
+ return value as string;
95
+ };
96
+
97
+ const validateDryRun = (value: unknown): boolean => {
98
+ return value as boolean;
99
+ };
100
+
101
+ const validateAccess = (value: unknown): Access | undefined => {
102
+ if (
103
+ value === undefined ||
104
+ value === ACCESS_PUBLIC ||
105
+ value === ACCESS_RESTRICTED
106
+ ) {
107
+ return value;
108
+ }
109
+
110
+ throw new errors.InvalidAccessError(value);
111
+ };
112
+
113
+ const validateStrategy = (value: unknown): Strategy => {
114
+ if (value === STRATEGY_ALL || value === STRATEGY_UPGRADE) {
115
+ return value;
116
+ }
117
+
118
+ throw new errors.InvalidStrategyError(value);
119
+ };
@@ -0,0 +1,98 @@
1
+ import childProcess from "node:child_process";
2
+ import os from "node:os";
3
+
4
+ import * as errors from "../errors.js";
5
+ import type { Logger } from "../options.js";
6
+ import type { NpmCliEnvironment } from "./use-npm-environment.js";
7
+
8
+ export interface NpmCliOptions<TReturn> {
9
+ environment?: NpmCliEnvironment;
10
+ ifError?: Record<string, TReturn>;
11
+ logger?: Logger | undefined;
12
+ }
13
+
14
+ const NPM = os.platform() === "win32" ? "npm.cmd" : "npm";
15
+ const JSON_MATCH_RE = /(\{[\s\S]*\})/mu;
16
+
17
+ const execNpm = (
18
+ commandArguments: string[],
19
+ environment: Record<string, string> = {},
20
+ logger?: Logger
21
+ ): Promise<{ stdout: string; stderr: string; exitCode: number }> => {
22
+ logger?.debug?.(`Running command: ${NPM} ${commandArguments.join(" ")}`);
23
+
24
+ return new Promise((resolve) => {
25
+ let stdout = "";
26
+ let stderr = "";
27
+
28
+ const npm = childProcess.spawn(NPM, commandArguments, {
29
+ env: { ...process.env, ...environment },
30
+ });
31
+
32
+ npm.stdout.on("data", (data) => (stdout += data));
33
+ npm.stderr.on("data", (data) => (stderr += data));
34
+ npm.on("close", (code) => {
35
+ resolve({
36
+ stdout: stdout.trim(),
37
+ stderr: stderr.trim(),
38
+ exitCode: code ?? 0,
39
+ });
40
+ });
41
+ });
42
+ };
43
+
44
+ const parseJson = <TParsed>(...values: string[]): TParsed | undefined => {
45
+ for (const value of values) {
46
+ const jsonValue = JSON_MATCH_RE.exec(value)?.[1];
47
+
48
+ if (jsonValue) {
49
+ try {
50
+ return JSON.parse(jsonValue) as TParsed;
51
+ } catch {
52
+ return undefined;
53
+ }
54
+ }
55
+ }
56
+
57
+ return undefined;
58
+ };
59
+
60
+ /**
61
+ * Call the NPM CLI in JSON mode.
62
+ *
63
+ * @param command The command of the NPM CLI to call
64
+ * @param cliArguments Any arguments to send to the command
65
+ * @param options Customize environment variables or add an error handler.
66
+ * @returns The parsed JSON, or stdout if unparsable.
67
+ */
68
+ export async function callNpmCli<TReturn = string>(
69
+ command: string,
70
+ cliArguments: string[],
71
+ options: NpmCliOptions<TReturn> = {}
72
+ ): Promise<TReturn> {
73
+ const { stdout, stderr, exitCode } = await execNpm(
74
+ [command, "--ignore-scripts", "--json", ...cliArguments],
75
+ options.environment,
76
+ options.logger
77
+ );
78
+
79
+ if (exitCode !== 0) {
80
+ const errorPayload = parseJson<{ error?: { code?: string | null } }>(
81
+ stdout,
82
+ stderr
83
+ );
84
+ const errorCode = errorPayload?.error?.code?.toLowerCase();
85
+
86
+ if (
87
+ typeof errorCode === "string" &&
88
+ options.ifError &&
89
+ errorCode in options.ifError
90
+ ) {
91
+ return options.ifError[errorCode] as TReturn;
92
+ }
93
+
94
+ throw new errors.NpmCallError(command, exitCode, stderr);
95
+ }
96
+
97
+ return parseJson(stdout) ?? (stdout as unknown as TReturn);
98
+ }
@@ -0,0 +1,34 @@
1
+ import type { NormalizedOptions } from "../normalize-options.js";
2
+
3
+ /**
4
+ * Given a publish configuration, get the NPM CLI publish arguments.
5
+ *
6
+ * @param packageSpec Package specification path.
7
+ * @param options Publish configuration.
8
+ * @returns Arguments to pass to the NPM CLI.
9
+ */
10
+ export function getPublishArguments(
11
+ packageSpec: string,
12
+ options: NormalizedOptions
13
+ ): string[] {
14
+ const { tag, access, dryRun } = options;
15
+ const publishArguments = [];
16
+
17
+ if (packageSpec.length > 0) {
18
+ publishArguments.push(packageSpec);
19
+ }
20
+
21
+ if (!tag.isDefault) {
22
+ publishArguments.push("--tag", tag.value);
23
+ }
24
+
25
+ if (!access.isDefault && access.value) {
26
+ publishArguments.push("--access", access.value);
27
+ }
28
+
29
+ if (dryRun.value) {
30
+ publishArguments.push("--dry-run");
31
+ }
32
+
33
+ return publishArguments;
34
+ }
@@ -0,0 +1,64 @@
1
+ import type { NormalizedOptions } from "../normalize-options.js";
2
+ import { useNpmEnvironment } from "./use-npm-environment.js";
3
+ import { callNpmCli } from "./call-npm-cli.js";
4
+ import { getPublishArguments } from "./get-publish-arguments.js";
5
+
6
+ export interface PublishedVersions {
7
+ "dist-tags": Record<string, string>;
8
+ versions: string[];
9
+ }
10
+
11
+ export interface PublishFile {
12
+ path: string;
13
+ size: number;
14
+ }
15
+
16
+ export interface PublishResult {
17
+ id: string;
18
+ files: PublishFile[];
19
+ }
20
+
21
+ /**
22
+ * Get a package's published versions.
23
+ *
24
+ * @param packageName The name of the package to get published versions for.
25
+ * @param options Configuration options.
26
+ * @returns All published versions and tags.
27
+ */
28
+ export async function getVersions(
29
+ packageName: string,
30
+ options: NormalizedOptions
31
+ ): Promise<PublishedVersions> {
32
+ return useNpmEnvironment(options, (environment) => {
33
+ return callNpmCli<PublishedVersions>(
34
+ "view",
35
+ [packageName, "dist-tags", "versions"],
36
+ {
37
+ logger: options.logger,
38
+ environment,
39
+ ifError: { e404: { "dist-tags": {}, versions: [] } },
40
+ }
41
+ );
42
+ });
43
+ }
44
+
45
+ /**
46
+ * Publish a package.
47
+ *
48
+ * @param packageSpec Package specification to pass to npm.
49
+ * @param options Configuration options.
50
+ * @returns Release metadata.
51
+ */
52
+ export async function publish(
53
+ packageSpec: string,
54
+ options: NormalizedOptions
55
+ ): Promise<PublishResult> {
56
+ const publishArguments = getPublishArguments(packageSpec, options);
57
+
58
+ return useNpmEnvironment(options, (environment) => {
59
+ return callNpmCli<PublishResult>("publish", publishArguments, {
60
+ logger: options.logger,
61
+ environment,
62
+ });
63
+ });
64
+ }