1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.flatten = exports.wait = exports.filterAsync = exports.setExtend = exports.Scratch = exports.slugify = exports.shell = exports.retry = exports.AllAttemptsFailed = exports.findUp = exports.findPackageJsonUp = exports.isBuiltinModule = exports.findDependencyDirectory = void 0;
|
4 | const child_process_1 = require("child_process");
|
5 | const fs = require("fs-extra");
|
6 | const os = require("os");
|
7 | const path = require("path");
|
8 | const logging = require("./logging");
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | async function findDependencyDirectory(dependencyName, searchStart) {
|
16 |
|
17 |
|
18 | const entryPoint = require.resolve(dependencyName, {
|
19 | paths: [searchStart],
|
20 | });
|
21 |
|
22 |
|
23 | const depPkgJsonPath = await findPackageJsonUp(dependencyName, path.dirname(entryPoint));
|
24 | if (!depPkgJsonPath) {
|
25 | throw new Error(`Could not find dependency '${dependencyName}' from '${searchStart}'`);
|
26 | }
|
27 | return depPkgJsonPath;
|
28 | }
|
29 | exports.findDependencyDirectory = findDependencyDirectory;
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 | function isBuiltinModule(depName) {
|
37 |
|
38 | const { builtinModules } = require('module');
|
39 | return (builtinModules ?? []).includes(depName);
|
40 | }
|
41 | exports.isBuiltinModule = isBuiltinModule;
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 | async function findPackageJsonUp(packageName, directory) {
|
49 | return findUp(directory, async (dir) => {
|
50 | const pjFile = path.join(dir, 'package.json');
|
51 | return ((await fs.pathExists(pjFile)) &&
|
52 | (await fs.readJson(pjFile)).name === packageName);
|
53 | });
|
54 | }
|
55 | exports.findPackageJsonUp = findPackageJsonUp;
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 | async function findUp(directory, pred) {
|
65 |
|
66 | while (true) {
|
67 |
|
68 | if (await pred(directory)) {
|
69 | return directory;
|
70 | }
|
71 | const parent = path.dirname(directory);
|
72 | if (parent === directory) {
|
73 | return undefined;
|
74 | }
|
75 | directory = parent;
|
76 | }
|
77 | }
|
78 | exports.findUp = findUp;
|
79 | class AllAttemptsFailed extends Error {
|
80 | constructor(callback, errors) {
|
81 | super(`All attempts failed. Last error: ${errors[errors.length - 1].message}`);
|
82 | this.callback = callback;
|
83 | this.errors = errors;
|
84 | }
|
85 | }
|
86 | exports.AllAttemptsFailed = AllAttemptsFailed;
|
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 |
|
95 | async function retry(cb, opts = {}, waiter = wait) {
|
96 | let attemptsLeft = opts.maxAttempts ?? 5;
|
97 | let backoffMs = opts.backoffBaseMilliseconds ?? 150;
|
98 | const backoffMult = opts.backoffMultiplier ?? 2;
|
99 |
|
100 | if (attemptsLeft <= 0) {
|
101 | throw new Error('maxTries must be > 0');
|
102 | }
|
103 | if (backoffMs <= 0) {
|
104 | throw new Error('backoffBaseMilliseconds must be > 0');
|
105 | }
|
106 | if (backoffMult <= 1) {
|
107 | throw new Error('backoffMultiplier must be > 1');
|
108 | }
|
109 | const errors = new Array();
|
110 | while (attemptsLeft > 0) {
|
111 | attemptsLeft--;
|
112 | try {
|
113 |
|
114 | return await cb();
|
115 | }
|
116 | catch (error) {
|
117 | errors.push(error);
|
118 | if (opts.onFailedAttempt != null) {
|
119 | opts.onFailedAttempt(error, attemptsLeft, backoffMs);
|
120 | }
|
121 | }
|
122 | if (attemptsLeft > 0) {
|
123 |
|
124 | await waiter(backoffMs).then(() => (backoffMs *= backoffMult));
|
125 | }
|
126 | }
|
127 | return Promise.reject(new AllAttemptsFailed(cb, errors));
|
128 | }
|
129 | exports.retry = retry;
|
130 |
|
131 |
|
132 |
|
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 |
|
140 | async function shell(cmd, args, { retry: retryOptions, ...options } = {}) {
|
141 | async function spawn1() {
|
142 | logging.debug(cmd, args.join(' '), JSON.stringify(options));
|
143 | return new Promise((ok, ko) => {
|
144 | const child = (0, child_process_1.spawn)(cmd, args, {
|
145 | ...options,
|
146 | shell: true,
|
147 | env: { ...process.env, ...(options.env ?? {}) },
|
148 | stdio: ['ignore', 'pipe', 'pipe'],
|
149 | });
|
150 | const stdout = new Array();
|
151 | const stderr = new Array();
|
152 | child.stdout.on('data', (chunk) => {
|
153 | if (logging.level.valueOf() >= logging.LEVEL_SILLY) {
|
154 | process.stderr.write(chunk);
|
155 | }
|
156 | stdout.push(Buffer.from(chunk));
|
157 | });
|
158 | child.stderr.on('data', (chunk) => {
|
159 | if (logging.level.valueOf() >= logging.LEVEL_SILLY) {
|
160 | process.stderr.write(chunk);
|
161 | }
|
162 | stderr.push(Buffer.from(chunk));
|
163 | });
|
164 | child.once('error', ko);
|
165 |
|
166 |
|
167 | child.once('close', (code, signal) => {
|
168 | const out = Buffer.concat(stdout).toString('utf-8');
|
169 | if (code === 0) {
|
170 | return ok(out);
|
171 | }
|
172 | const err = Buffer.concat(stderr).toString('utf-8');
|
173 | const reason = signal != null ? `signal ${signal}` : `status ${code}`;
|
174 | const command = `${cmd} ${args.join(' ')}`;
|
175 | return ko(new Error([
|
176 | `Command (${command}) failed with ${reason}:`,
|
177 |
|
178 | prefix(err, '#STDERR> '),
|
179 | prefix(out, '#STDOUT> '),
|
180 | ].join('\n')));
|
181 | function prefix(text, add) {
|
182 | return text
|
183 | .split('\n')
|
184 | .map((line) => `${add}${line}`)
|
185 | .join('\n');
|
186 | }
|
187 | });
|
188 | });
|
189 | }
|
190 | if (retryOptions != null) {
|
191 | return retry(spawn1, {
|
192 | ...retryOptions,
|
193 | onFailedAttempt: retryOptions.onFailedAttempt ??
|
194 | ((error, attemptsLeft, backoffMs) => {
|
195 | const message = error.message ?? error;
|
196 | const retryInfo = attemptsLeft > 0
|
197 | ? `Waiting ${backoffMs} ms before retrying (${attemptsLeft} attempts left)`
|
198 | : 'No attempts left';
|
199 | logging.info(`Command "${cmd} ${args.join(' ')}" failed with ${message}. ${retryInfo}.`);
|
200 | }),
|
201 | });
|
202 | }
|
203 | return spawn1();
|
204 | }
|
205 | exports.shell = shell;
|
206 |
|
207 |
|
208 |
|
209 | function slugify(x) {
|
210 | return x.replace(/[^a-zA-Z0-9_-]/g, '_');
|
211 | }
|
212 | exports.slugify = slugify;
|
213 |
|
214 |
|
215 |
|
216 | class Scratch {
|
217 | constructor(directory, object, fake) {
|
218 | this.directory = directory;
|
219 | this.object = object;
|
220 | this.fake = fake;
|
221 | }
|
222 | static async make(factory) {
|
223 | const tmpdir = await fs.mkdtemp(path.join(os.tmpdir(), 'npm-pack'));
|
224 | return new Scratch(tmpdir, await factory(tmpdir), false);
|
225 | }
|
226 | static fake(directory, object) {
|
227 | return new Scratch(directory, object, true);
|
228 | }
|
229 | static async cleanupAll(tempDirs) {
|
230 | await Promise.all(tempDirs.map((t) => t.cleanup()));
|
231 | }
|
232 | async cleanup() {
|
233 | if (!this.fake) {
|
234 | try {
|
235 | await fs.remove(this.directory);
|
236 | }
|
237 | catch (e) {
|
238 | if (e.code === 'EBUSY') {
|
239 |
|
240 |
|
241 |
|
242 |
|
243 | await new Promise((ok) => setTimeout(ok, 1000));
|
244 | try {
|
245 | await fs.remove(this.directory);
|
246 | }
|
247 | catch (e2) {
|
248 | logging.warn(`Unable to clean up ${this.directory}: ${e2}`);
|
249 | }
|
250 | return;
|
251 | }
|
252 | logging.warn(`Unable to clean up ${this.directory}: ${e}`);
|
253 | }
|
254 | }
|
255 | }
|
256 | }
|
257 | exports.Scratch = Scratch;
|
258 | function setExtend(xs, els) {
|
259 | for (const el of els) {
|
260 | xs.add(el);
|
261 | }
|
262 | }
|
263 | exports.setExtend = setExtend;
|
264 | async function filterAsync(xs, pred) {
|
265 | const mapped = await Promise.all(xs.map(async (x) => ({ x, pred: await pred(x) })));
|
266 | return mapped.filter(({ pred }) => pred).map(({ x }) => x);
|
267 | }
|
268 | exports.filterAsync = filterAsync;
|
269 | async function wait(ms) {
|
270 | return new Promise((ok) => setTimeout(ok, ms));
|
271 | }
|
272 | exports.wait = wait;
|
273 | function flatten(xs) {
|
274 | return Array.prototype.concat.call([], ...xs);
|
275 | }
|
276 | exports.flatten = flatten;
|
277 |
|
\ | No newline at end of file |