UNPKG

11.2 kBJavaScriptView Raw
1"use strict";
2var __importDefault = (this && this.__importDefault) || function (mod) {
3 return (mod && mod.__esModule) ? mod : { "default": mod };
4};
5Object.defineProperty(exports, "__esModule", { value: true });
6exports.installDependencies = exports.runPackageJsonScript = exports.runPipInstall = exports.runBundleInstall = exports.runNpmInstall = exports.walkParentDirs = exports.getNodeVersion = exports.getSpawnOptions = exports.runShellScript = exports.getNodeBinPath = exports.execCommand = exports.spawnCommand = exports.execAsync = exports.spawnAsync = void 0;
7const assert_1 = __importDefault(require("assert"));
8const fs_extra_1 = __importDefault(require("fs-extra"));
9const path_1 = __importDefault(require("path"));
10const debug_1 = __importDefault(require("../debug"));
11const cross_spawn_1 = __importDefault(require("cross-spawn"));
12const util_1 = require("util");
13const os_1 = require("os");
14const errors_1 = require("../errors");
15const node_version_1 = require("./node-version");
16function spawnAsync(command, args, opts = {}) {
17 return new Promise((resolve, reject) => {
18 const stderrLogs = [];
19 opts = { stdio: 'inherit', ...opts };
20 const child = cross_spawn_1.default(command, args, opts);
21 if (opts.stdio === 'pipe' && child.stderr) {
22 child.stderr.on('data', data => stderrLogs.push(data));
23 }
24 child.on('error', reject);
25 child.on('close', (code, signal) => {
26 if (code === 0) {
27 return resolve();
28 }
29 const cmd = opts.prettyCommand
30 ? `Command "${opts.prettyCommand}"`
31 : 'Command';
32 reject(new errors_1.NowBuildError({
33 code: `BUILD_UTILS_SPAWN_${code || signal}`,
34 message: opts.stdio === 'inherit'
35 ? `${cmd} exited with ${code || signal}`
36 : stderrLogs.map(line => line.toString()).join(''),
37 }));
38 });
39 });
40}
41exports.spawnAsync = spawnAsync;
42function execAsync(command, args, opts = {}) {
43 return new Promise((resolve, reject) => {
44 opts.stdio = 'pipe';
45 const stdoutList = [];
46 const stderrList = [];
47 const child = cross_spawn_1.default(command, args, opts);
48 child.stderr.on('data', data => {
49 stderrList.push(data);
50 });
51 child.stdout.on('data', data => {
52 stdoutList.push(data);
53 });
54 child.on('error', reject);
55 child.on('close', (code, signal) => {
56 if (code !== 0) {
57 const cmd = opts.prettyCommand
58 ? `Command "${opts.prettyCommand}"`
59 : 'Command';
60 return reject(new errors_1.NowBuildError({
61 code: `BUILD_UTILS_EXEC_${code || signal}`,
62 message: `${cmd} exited with ${code || signal}`,
63 }));
64 }
65 return resolve({
66 code,
67 stdout: Buffer.concat(stdoutList).toString(),
68 stderr: Buffer.concat(stderrList).toString(),
69 });
70 });
71 });
72}
73exports.execAsync = execAsync;
74function spawnCommand(command, options = {}) {
75 const opts = { ...options, prettyCommand: command };
76 if (process.platform === 'win32') {
77 return cross_spawn_1.default('cmd.exe', ['/C', command], opts);
78 }
79 return cross_spawn_1.default('sh', ['-c', command], opts);
80}
81exports.spawnCommand = spawnCommand;
82async function execCommand(command, options = {}) {
83 const opts = { ...options, prettyCommand: command };
84 if (process.platform === 'win32') {
85 await spawnAsync('cmd.exe', ['/C', command], opts);
86 }
87 else {
88 await spawnAsync('sh', ['-c', command], opts);
89 }
90 return true;
91}
92exports.execCommand = execCommand;
93async function getNodeBinPath({ cwd }) {
94 const { stdout } = await execAsync('npm', ['bin'], { cwd });
95 return stdout.trim();
96}
97exports.getNodeBinPath = getNodeBinPath;
98async function chmodPlusX(fsPath) {
99 const s = await fs_extra_1.default.stat(fsPath);
100 const newMode = s.mode | 64 | 8 | 1; // eslint-disable-line no-bitwise
101 if (s.mode === newMode)
102 return;
103 const base8 = newMode.toString(8).slice(-3);
104 await fs_extra_1.default.chmod(fsPath, base8);
105}
106async function runShellScript(fsPath, args = [], spawnOpts) {
107 assert_1.default(path_1.default.isAbsolute(fsPath));
108 const destPath = path_1.default.dirname(fsPath);
109 await chmodPlusX(fsPath);
110 const command = `./${path_1.default.basename(fsPath)}`;
111 await spawnAsync(command, args, {
112 ...spawnOpts,
113 cwd: destPath,
114 prettyCommand: command,
115 });
116 return true;
117}
118exports.runShellScript = runShellScript;
119function getSpawnOptions(meta, nodeVersion) {
120 const opts = {
121 env: { ...process.env },
122 };
123 if (!meta.isDev) {
124 opts.env.PATH = `/node${nodeVersion.major}/bin:${opts.env.PATH}`;
125 }
126 return opts;
127}
128exports.getSpawnOptions = getSpawnOptions;
129async function getNodeVersion(destPath, _nodeVersion, _config, meta) {
130 if (meta && meta.isDev) {
131 // Use the system-installed version of `node` in PATH for `vercel dev`
132 const latest = node_version_1.getLatestNodeVersion();
133 return { ...latest, runtime: 'nodejs' };
134 }
135 const { packageJson } = await scanParentDirs(destPath, true);
136 let range;
137 let isAuto = true;
138 if (packageJson && packageJson.engines && packageJson.engines.node) {
139 range = packageJson.engines.node;
140 isAuto = false;
141 }
142 return node_version_1.getSupportedNodeVersion(range, isAuto);
143}
144exports.getNodeVersion = getNodeVersion;
145async function scanParentDirs(destPath, readPackageJson = false) {
146 assert_1.default(path_1.default.isAbsolute(destPath));
147 let cliType = 'yarn';
148 let packageJson;
149 let currentDestPath = destPath;
150 // eslint-disable-next-line no-constant-condition
151 while (true) {
152 const packageJsonPath = path_1.default.join(currentDestPath, 'package.json');
153 // eslint-disable-next-line no-await-in-loop
154 if (await fs_extra_1.default.pathExists(packageJsonPath)) {
155 // eslint-disable-next-line no-await-in-loop
156 if (readPackageJson) {
157 packageJson = JSON.parse(await fs_extra_1.default.readFile(packageJsonPath, 'utf8'));
158 }
159 // eslint-disable-next-line no-await-in-loop
160 const [hasPackageLockJson, hasYarnLock] = await Promise.all([
161 fs_extra_1.default.pathExists(path_1.default.join(currentDestPath, 'package-lock.json')),
162 fs_extra_1.default.pathExists(path_1.default.join(currentDestPath, 'yarn.lock')),
163 ]);
164 if (hasPackageLockJson && !hasYarnLock) {
165 cliType = 'npm';
166 }
167 break;
168 }
169 const newDestPath = path_1.default.dirname(currentDestPath);
170 if (currentDestPath === newDestPath)
171 break;
172 currentDestPath = newDestPath;
173 }
174 return { cliType, packageJson };
175}
176async function walkParentDirs({ base, start, filename, }) {
177 assert_1.default(path_1.default.isAbsolute(base), 'Expected "base" to be absolute path');
178 assert_1.default(path_1.default.isAbsolute(start), 'Expected "start" to be absolute path');
179 let parent = '';
180 for (let current = start; base.length <= current.length; current = parent) {
181 const fullPath = path_1.default.join(current, filename);
182 // eslint-disable-next-line no-await-in-loop
183 if (await fs_extra_1.default.pathExists(fullPath)) {
184 return fullPath;
185 }
186 parent = path_1.default.dirname(current);
187 }
188 return null;
189}
190exports.walkParentDirs = walkParentDirs;
191async function runNpmInstall(destPath, args = [], spawnOpts, meta) {
192 if (meta && meta.isDev) {
193 debug_1.default('Skipping dependency installation because dev mode is enabled');
194 return;
195 }
196 assert_1.default(path_1.default.isAbsolute(destPath));
197 debug_1.default(`Installing to ${destPath}`);
198 const { cliType } = await scanParentDirs(destPath);
199 const opts = { cwd: destPath, ...spawnOpts };
200 const env = opts.env ? { ...opts.env } : { ...process.env };
201 delete env.NODE_ENV;
202 opts.env = env;
203 let command;
204 let commandArgs;
205 if (cliType === 'npm') {
206 opts.prettyCommand = 'npm install';
207 command = 'npm';
208 commandArgs = args
209 .filter(a => a !== '--prefer-offline')
210 .concat(['install', '--no-audit', '--unsafe-perm']);
211 }
212 else {
213 opts.prettyCommand = 'yarn install';
214 command = 'yarn';
215 commandArgs = ['install', ...args];
216 }
217 if (process.env.NPM_ONLY_PRODUCTION) {
218 commandArgs.push('--production');
219 }
220 await spawnAsync(command, commandArgs, opts);
221}
222exports.runNpmInstall = runNpmInstall;
223async function runBundleInstall(destPath, args = [], spawnOpts, meta) {
224 if (meta && meta.isDev) {
225 debug_1.default('Skipping dependency installation because dev mode is enabled');
226 return;
227 }
228 assert_1.default(path_1.default.isAbsolute(destPath));
229 const opts = { ...spawnOpts, cwd: destPath, prettyCommand: 'bundle install' };
230 await spawnAsync('bundle', args.concat([
231 'install',
232 '--no-prune',
233 '--retry',
234 '3',
235 '--jobs',
236 String(os_1.cpus().length || 1),
237 ]), opts);
238}
239exports.runBundleInstall = runBundleInstall;
240async function runPipInstall(destPath, args = [], spawnOpts, meta) {
241 if (meta && meta.isDev) {
242 debug_1.default('Skipping dependency installation because dev mode is enabled');
243 return;
244 }
245 assert_1.default(path_1.default.isAbsolute(destPath));
246 const opts = { ...spawnOpts, cwd: destPath, prettyCommand: 'pip3 install' };
247 await spawnAsync('pip3', ['install', '--disable-pip-version-check', ...args], opts);
248}
249exports.runPipInstall = runPipInstall;
250async function runPackageJsonScript(destPath, scriptName, spawnOpts) {
251 assert_1.default(path_1.default.isAbsolute(destPath));
252 const { packageJson, cliType } = await scanParentDirs(destPath, true);
253 const hasScript = Boolean(packageJson &&
254 packageJson.scripts &&
255 scriptName &&
256 packageJson.scripts[scriptName]);
257 if (!hasScript)
258 return false;
259 if (cliType === 'npm') {
260 const prettyCommand = `npm run ${scriptName}`;
261 console.log(`Running "${prettyCommand}"`);
262 await spawnAsync('npm', ['run', scriptName], {
263 ...spawnOpts,
264 cwd: destPath,
265 prettyCommand,
266 });
267 }
268 else {
269 const prettyCommand = `yarn run ${scriptName}`;
270 console.log(`Running "${prettyCommand}"`);
271 await spawnAsync('yarn', ['run', scriptName], {
272 ...spawnOpts,
273 cwd: destPath,
274 prettyCommand,
275 });
276 }
277 return true;
278}
279exports.runPackageJsonScript = runPackageJsonScript;
280/**
281 * @deprecate installDependencies() is deprecated.
282 * Please use runNpmInstall() instead.
283 */
284exports.installDependencies = util_1.deprecate(runNpmInstall, 'installDependencies() is deprecated. Please use runNpmInstall() instead.');