Add something like `$.multiline` for multiline commands?
I saw #334, but I think that it could be better to simplify it - at least, avoid extra backslashes in the tag and extra spaces in the created command.
For example, in
await $`test262-harness \
--threads=${ os.cpus().length } \
--host-args="--unhandled-rejections=none" \
--preprocessor=preprocessor.js \
--prelude=../../packages/core-js-bundle/index.js \
--test262-dir=node_modules/test262 \
node_modules/test262/test/built-ins/**/*.js \
`;
backslashes make the code less readable and create
test262-harness --threads=8 --host-args="--unhandled-rejections=none" --preprocessor=preprocessor.js --prelude=../../packages/core-js-bundle/index.js --test262-dir=node_modules/test262 node_modules/test262/test/built-ins/**/*.js
command with many extra spaces that is also not good for readability.
What about using an array? This feature is already implemented, avoid backslashes and "extra spaces", allow comments, and doesn't break the ability of running more than one command in the same $ call.
await $`test262-harness ${[
`--threads=${ os.cpus().length }`,
'--host-args="--unhandled-rejections=none"',
'--preprocessor=preprocessor.js',
'--prelude=../../packages/core-js-bundle/index.js',
'--test262-dir=node_modules/test262',
'node_modules/test262/test/built-ins/**/*.js',
// This should do what you expect, doesn't it?
]}`
@pizzafroide 12 extra quotes, 6 extra commas, 2 extra square brackets, 2 extra curly brackets, and 1 extra dollar sign? 23(!!!) extra special symbols have a great effect on the readability of the command. I don't think that it's something that I want.
Fair enough! 😄
Although, I don't quite agree that more characters necessarily means less readability. As a matter of fact, if that was the case, $.multiline should only be used for commands using 10 backslashes or more. Actually, the backslashes clearly state that this is a multiline command which, imo, is more readable than no backslashes at all.
On the other hand, it is true that all these unnecessary spaces (when using backslashes) makes the final command output ugly. $.multiline would clearly be useful for that matter.
So what about something like this:
$.multiline = (strings, ...values) =>
$(
strings.map((s) => s.replace(/\s+/g, ' ')),
...values
)
await $.multiline`echo \
foo \
bar \
`
It allows using backslashes or not using them (you decide), and the command output is prettier...
What is the argument against replacing backslash-newline-spaces with a single space by default?
@nichtich too many spaces, like in the example from the top?
I thought to do it in in $ with something like
// remove linebreaks and whitespace if preceded by line continuation character \
pieces = pieces.map(p => p.replaceAll(/\s*\\(\r\n|\n|\r)\s*/gm,' '))
What about using an array?
This interacts awkwardly with the quoting behavior. The README shows this example:
let flags = [
'--oneline',
'--decorate',
'--color',
]
await $`git log ${flags}`
But users may be surprised (as I was) to discover that this doesn't work:
const curlArgs = [
"-sLX POST",
'--header "Content-Type: application/json"',
'--data "{}"',
"example.com",
];
const curlResult = await $`curl ${curlArgs}`; // fails
The reason is that each argument in the array is quoted, so curl throws the error option --header "Content-Type: application/json": is unknown. Using curlArgs.join(' ') doesn't work, either, since curl will just see one big, quoted arg.
I don't see anything in zx's docs about how to pass args to shell commands that use the common --key value convention. Am I missing something? (Happy to start a Discussion if you'd prefer.)
--key and value should be two separated items in curlArgs. Try:
const curlArgs = [
'-sLX', 'POST',
'--header', 'Content-Type: application/json',
'--data', '{}',
'example.com',
]
const curlResult = await $`curl ${curlArgs}`;
@pizzafroide Thanks! Somehow I didn't think of that. 😅 Though, on the topic of this ticket, that's an example where the array format isn't an ideal way of expressing multiline commands: Prettier is going to force each arg to a separate line, breaking the visual connection between key-value pairings. (Sure, you don't have to use Prettier… but being able to use Prettier is one of the great perks of using zx IMO!) A nice solution to the Prettier issue would be for zx to automatically flatten nested arrays so that they could be used to group arguments together:
const curlArgs = [
['-sLX', 'POST'],
['--header', 'Content-Type: application/json'],
['--data', '{}'],
'example.com',
]
const curlResult = await $`curl ${curlArgs}`;
Also, I was a little surprised that this didn't work:
const curlArgs = [
'--cookie', '$HOME/.cookie',
'example.com',
]
const curlResult = await $`curl ${curlArgs}`;
The quoting docs note that globs don't work, but don't mention that env vars (like $HOME) also don't work. Since using env vars in shell commands is very common, it might be nice to include an example of this in the docs. I was able to figure out this solution:
const curlArgs = [
'--cookie', `${$.env.HOME}/.cookie`,
'example.com',
]
const curlResult = await $`curl ${curlArgs}`;
Well, this is indeed a little off-topic. It is interesting, though! So I've started a discussion about an idea I had related to that kind of things! You can check it out here: #592.
Is this completed? I don't see anything about it.
This feature will be in v8.