/
npm-publish.ts
109 lines (93 loc) · 3.5 KB
/
npm-publish.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import { LifecycleConfig, Package, RawManifest, runLifecycle } from '@lerna-lite/core';
import { OneTimePasswordCache, otplease } from '@lerna-lite/version';
import { readFile } from 'fs/promises';
import log from 'npmlog';
import npa from 'npm-package-arg';
import { join } from 'node:path';
import pify from 'pify';
import { publish } from 'libnpmpublish';
import readJSON from 'read-package-json';
import { LibNpmPublishOptions, PackagePublishConfig } from '../models/index.js';
const readJSONAsync = pify(readJSON);
/**
* Alias dash-cased npmConf to camelCase
* @param {NpmPublishOptions} obj
* @returns {NpmPublishOptions}
*/
function flattenOptions(obj: Omit<LibNpmPublishOptions, 'defaultTag'>): LibNpmPublishOptions {
return {
defaultTag: obj['tag'] || 'latest',
dryRun: obj['dry-run'] || obj['git-dry-run'],
// libnpmpublish / npm-registry-fetch check strictSSL rather than strict-ssl
strictSSL: obj['strict-ssl'],
...obj,
};
}
/**
* Publish a package to the configured registry.
* @param {import("@lerna/package").Package} pkg
* @param {string} tarFilePath
* @param {LibNpmPublishOptions & NpmPublishOptions} [options]
* @param {import("@lerna/otplease").OneTimePasswordCache} [otpCache]
*/
export function npmPublish(
pkg: Package,
tarFilePath: string,
options: Omit<LibNpmPublishOptions, 'defaultTag'> = {},
otpCache?: OneTimePasswordCache
) {
const { dryRun, ...remainingOptions } = flattenOptions(options);
const { scope } = npa(pkg?.name ?? '');
// pass only the package scope to libnpmpublish
const opts = {
log,
...remainingOptions,
projectScope: scope,
};
opts.log.verbose('publish', pkg.name);
let chain: Promise<any> = Promise.resolve();
if (!dryRun) {
chain = chain.then(() => {
let { manifestLocation } = pkg;
if (pkg.contents !== pkg.location) {
// 'rebase' manifest used to generated directory
manifestLocation = join(pkg.contents, 'package.json');
}
return Promise.all([readFile(tarFilePath), readJSONAsync(manifestLocation) as RawManifest]);
});
chain = chain.then(([tarData, manifest]: [any, RawManifest]) => {
// non-default tag needs to override publishConfig.tag,
// which is merged into opts below if necessary
if (
opts.defaultTag !== 'latest' &&
manifest.publishConfig &&
manifest.publishConfig.tag &&
manifest.publishConfig.tag !== opts.defaultTag
) {
manifest.publishConfig.tag = opts.defaultTag as string;
}
// publishConfig is no longer consumed in n-r-f, so merge here
if (manifest.publishConfig) {
Object.assign(opts, publishConfigToOpts(manifest.publishConfig));
}
return otplease((innerOpts) => publish(manifest, tarData, innerOpts), opts, otpCache as OneTimePasswordCache);
});
}
chain = chain.then(() => runLifecycle(pkg, 'publish', opts as LifecycleConfig));
chain = chain.then(() => runLifecycle(pkg, 'postpublish', opts as LifecycleConfig));
return chain;
}
/**
* Obtain an object suitable for assignment onto existing options from `pkg.publishConfig`.
* @param {PackagePublishConfig} publishConfig
* @returns {Omit<PackagePublishConfig, 'tag'> & { defaultTag?: string }}
*/
function publishConfigToOpts(publishConfig: PackagePublishConfig): Omit<PackagePublishConfig, 'tag'> & { defaultTag?: string } {
const opts = { ...publishConfig };
// npm v7 renamed tag internally
if (publishConfig.tag) {
opts.defaultTag = publishConfig.tag;
delete opts.tag;
}
return opts;
}