-
Notifications
You must be signed in to change notification settings - Fork 2.2k
/
npm-publish.js
164 lines (131 loc) · 4.88 KB
/
npm-publish.js
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
"use strict";
const fs = require("fs-extra");
const log = require("npmlog");
const path = require("path");
const pMap = require("p-map");
const ChildProcessUtilities = require("@lerna/child-process");
const getExecOpts = require("@lerna/get-npm-exec-opts");
const hasNpmVersion = require("@lerna/has-npm-version");
const logPacked = require("@lerna/log-packed");
module.exports = npmPublish;
module.exports.npmPack = npmPack;
module.exports.makePacker = makePacker;
function npmPublish(pkg, tag, { npmClient, registry }) {
log.verbose("publish", pkg.name);
const distTag = tag && tag.trim();
const opts = getExecOpts(pkg, registry);
const args = ["publish", "--ignore-scripts"];
if (distTag) {
args.push("--tag", distTag);
}
if (npmClient === "yarn") {
// skip prompt for new version, use existing instead
// https://yarnpkg.com/en/docs/cli/publish#toc-yarn-publish-new-version
args.push("--new-version", pkg.version, "--non-interactive");
}
// always add tarball file, created by npmPack()
args.push(pkg.tarball.filename);
log.silly("exec", npmClient, args);
return ChildProcessUtilities.exec(npmClient, args, opts).then(() =>
// don't leave the generated tarball hanging around after success
fs.remove(path.join(pkg.location, pkg.tarball.filename))
);
}
function makePackOptions(rootManifest) {
const opts = getExecOpts(rootManifest);
// let that juicy npm logging have its day in the sun
opts.stdio = ["ignore", "pipe", "inherit"];
// no need for fancy error wrapping
delete opts.pkg;
if (hasNpmVersion(">=5.10.0")) {
// request JSON output for easier parsing of filenames
opts.env.npm_config_json = true;
}
/* istanbul ignore if */
if (process.env.LERNA_INTEGRATION) {
// override process.env.npm_config_dry_run from integration tests
opts.env.npm_config_dry_run = false;
}
log.silly("exec", "npm options", opts);
return opts;
}
function npmPack(rootManifest, packages, opts = makePackOptions(rootManifest)) {
// NOTE: All of the istanbul-ignored branches are covered in integration tests
log.verbose("pack", packages.map(p => p.name));
const args = ["pack"].concat(packages.map(p => p.location));
log.silly("exec", "npm", args);
const proc = ChildProcessUtilities.exec("npm", args, opts);
// --json output is an array of objects stringified with 2-space indent
const jsonStart = /^\[\n[ ]{2}{\n[ ]{4}/;
let jsonBegan = false;
let jsonString = "";
// npm-lifecycle log note should be removed when --loglevel=silent
// we cannot pass --silent because it also hides --json
const isNote = /^\n> /;
/* istanbul ignore next */
const output =
log.level !== "silent"
? chunk => process.stdout.write(chunk)
: chunk => !isNote.test(chunk) && process.stdout.write(chunk);
// only pipe script output instead of inheriting stdout
// stop piping when we reach the start of --json output
proc.stdout.setEncoding("utf8");
proc.stdout.on("data", chunk => {
if (jsonBegan) {
// it could be larger than a single chunk
jsonString += chunk;
} else if (jsonStart.test(chunk)) {
// the first json chunk (commonly the only one)
jsonString += chunk;
// stop writing output to stdout
jsonBegan = true;
} else {
// maybe write non-json chunk to stdout
log.pause();
output(chunk);
log.resume();
}
});
return proc.then(result => {
const tarballs = jsonString ? JSON.parse(jsonString) : parseLegacyTarballs(result, packages);
tarballs.forEach(logPacked);
// npm pack --json list is in the same order as the packages input
for (let i = 0; i < packages.length; i += 1) {
// could do this inside the mapper, but we need this metadata regardless of p-map success
const pkg = packages[i];
// instead of complicated copypasta, use literally what npm did
pkg.tarball = tarballs[i];
}
return pMap(
packages,
pkg => {
const inRoot = path.join(pkg.rootPath, pkg.tarball.filename);
const toLeaf = path.join(pkg.location, pkg.tarball.filename);
return fs.move(inRoot, toLeaf, { overwrite: true });
},
{ concurrency: 10 }
);
});
}
function makePacker(rootManifest) {
const opts = makePackOptions(rootManifest);
return packages => npmPack(rootManifest, packages, opts);
}
function parseLegacyTarballs(result, packages) {
// legacy `npm pack` outputs the generated tarball names
// at the end of stdout in the order of package input(s)
const isTgz = /^[\S]+\.tgz$/;
const lines = result.stdout.split("\n");
const files = lines.filter(line => isTgz.test(line));
// each result is passed to log-packed, so it needs decoration
return files.map((filename, idx) => {
const pkg = packages[idx];
return {
// the important part
filename,
// make log-packed show _something_ useful
name: pkg.name,
version: pkg.version,
};
});
}