blob: 1b588b69f27f22967410d69a3ca096aecccc53b3 [file] [log] [blame]
Leo Repp58b9f112021-11-22 11:57:47 +01001'use strict';
2const {exec} = require('child_process');
3const chalk = require('chalk');
4const stripAnsi = require('strip-ansi');
5const npmRunPath = require('npm-run-path');
6
7const TEN_MEGABYTES = 1000 * 1000 * 10;
8
9module.exports = grunt => {
10 grunt.registerMultiTask('shell', 'Run shell commands', function (...args) {
11 const callback = this.async();
12 const options = this.options({
13 stdout: true,
14 stderr: true,
15 stdin: true,
16 failOnError: true,
17 stdinRawMode: false,
18 preferLocal: true,
19 execOptions: {
20 env: null
21 }
22 });
23
24 let cmd = (typeof this.data === 'string' || typeof this.data === 'function') ?
25 this.data :
26 this.data.command;
27
28 if (cmd === undefined) {
29 throw new Error('`command` required');
30 }
31
32 // Increase max buffer
33 options.execOptions = Object.assign({}, options.execOptions);
34 options.execOptions.maxBuffer = options.execOptions.maxBuffer || TEN_MEGABYTES;
35
36 cmd = grunt.template.process(typeof cmd === 'function' ? cmd.apply(grunt, args) : cmd);
37
38 if (options.preferLocal === true) {
39 options.execOptions.env = npmRunPath.env({env: options.execOptions.env || process.env});
40 }
41
42 if (this.data.cwd) {
43 options.execOptions.cwd = this.data.cwd;
44 }
45
46 const cp = exec(cmd, options.execOptions, (error, stdout, stderr) => {
47 if (typeof options.callback === 'function') {
48 options.callback.call(this, error, stdout, stderr, callback);
49 } else {
50 if (error && options.failOnError) {
51 grunt.warn(error);
52 }
53 callback();
54 }
55 });
56
57 const captureOutput = (child, output) => {
58 if (grunt.option('color') === false) {
59 child.on('data', data => {
60 output.write(stripAnsi(data));
61 });
62 } else {
63 child.pipe(output);
64 }
65 };
66
67 grunt.verbose.writeln('Command:', chalk.yellow(cmd));
68
69 if (options.stdout || grunt.option('verbose')) {
70 captureOutput(cp.stdout, process.stdout);
71 }
72
73 if (options.stderr || grunt.option('verbose')) {
74 captureOutput(cp.stderr, process.stderr);
75 }
76
77 if (options.stdin) {
78 process.stdin.resume();
79 process.stdin.setEncoding('utf8');
80
81 if (options.stdinRawMode && process.stdin.isTTY) {
82 process.stdin.setRawMode(true);
83 }
84
85 process.stdin.pipe(cp.stdin);
86 }
87 });
88};