With pipes you can visually follow the manipulations and function calls in the order that they happen instead of being forced to scan the code inside-out & outside-in, matching parentheses and function call parameters in your head, while still visualizing intermediate results to get 1 final return value.
I find Elixir code much easier and quicker to understand, in large part thanks to its (admittedly, imperfect) pipe syntax. Code written in this way is also much easier to debug because you can quickly add `console.log`, breakpoints, or equivalent between the pipes.
I find this unnecessarily time-consuming and difficult to parse and I'd likely raise some flags in a code review:
console.log(chalk.dim(
`$ ${Object.keys(envars)
.map(envar => `${envar}=${envars[envar]}`)
.join(' ')
}`,
'node',
args.join(' ')
));
Without pipe syntax, I'd refactor this to: const envStr = Object.keys(envars)
.map(envar => `${envar}=${envars[envar]}`)
.join(' ');
const styled = chalk.dim(`$ ${envStr}`, 'node', args.join(' '));
console.log(styled);
But you often find yourself having to add additional logic, e.g. to scrub sensitive values, so it would probably end up closer to: const sensitiveEnv = [...];
const envStr = Object.keys(envars)
.filter(envar => !sensitiveEnv.includes(envar))
.map(envar => `${envar}=${envars[envar]}`)
.join(' ');
const styled = chalk.dim(`$ ${envStr}`, 'node', args.join(' '));
console.log(styled);