In this tweet I've shown how the incoming version of NodeJS could be used to run a standalone file as ESM through the flag --input-type=module.
This gist is an attempt to describe the technique I've used.
#!/usr/bin/env sh
J=S//;sed "1,2d" "$0"|echo "\n\n$(cat -)"|node --input-type=module "$@";exit $?
/* your 100% ESM code here */It's a regular hashbang, that could usually be written either as #!/bin/sh or, in a wider compatible way, as #!/usr/bin/env sh, to carry on the environment, and execute via sh, or even bash.
This first line requires that the file is executable, something easy to obtain via chmod, as in chmod +x filename.
Technically speaking, the J=S//; part of the string could be omitted, but then any JavaScript editor would show errors due usage of incompatible syntax.
Using J=S//; as prefix instead, would simply assign, in the shell world, the string S// to a variable J, but it won't trigger errors, neither it will ever execute, in the NodeJS world.
However, it will start an inline comment for your IDE of choice, so that no errors would be shown.
In shell world, it will simply keep executing whatever is after the semicolon.
Keep in mind, shell scripts, as well as bash scripts, execute as a stream, instead of requiring a while parsing, with syntax validation, upfront, meaning that exiting any file at any time, won't cause any syntax error for whatever content is presetn after the program ended.
The micro utility sed is a quite universal way to replace, hence transform, some content.
sed "1,2d" "$0"Above command strips out the first two lines from the file specified by $0. In shell, as well as in bash, it's always good to expand variables that could contain spaces via double quotes, which is why $0 is wrapped as "$0", so that any path would be accepted.
In shell, as in bash, the $0 refers to the file that is currently executing, like __filename would be in CommonJS.
In shell, as in bash, the pipe operator | streams output produced by the left hand side, as right hand side input.
In this example, the pipe passes the sed output, the file without the first two lines, to the echo command.
The echo command simply prints, usually via the standard output, some content.
The reason echo is part of this technique is that we'd like to maintain the line number, in case any error occurs during NodeJS execution.
I'm not sure sed could do that for me right away, but echo works just as well.
It's important to use doublequotes to concatenate two new lines, "\n\n", and the received standard input, throught the pipe, via cat.
In shell, as in bash, double quotes can contain the output produced by system calls, as it is for echo "1$(echo 2)3", simply outputting 123.
Similarly to sed, cat is the most common way to stream some content to the standard outoput.
You can, as example, see the content of any file via cat filename, but it's usually more handy combined with other tools, like less, as example, so that you can read through the console even big files via cat filename | less.
In this specific case, cat is used due its handy - feature that simply passes through whatever input the current execution is receiving.
To recap, echo "\n\n$(cat -)" will pass along an output containing two new lines \n plus whatever sed outputted before.
After long discussions, it became obvious that the previously proposed flag --entry-type=module would've caused more confusion than expected, because it wasn't equivalent to the "type": "module" field in a package.json.
Accordingly, since node --entry-type=module filename wouldn't have run filename as if it was within a module scoped as ESM via its package.json, they decide to remove such flag and use instead one that would only accept an input, which is specially handy for evaluation -e cases.
But fear not, this technique perfectly addresses that evaluation case, making the file itself, the source of the ESM code that will be evaluated.
node --input-type=module "$@"Above shell, or bash, command switches node input parsing goal as module, which practically means CommonJS is not available, and everything would run as pure ESM, even if an input couldn't really ever be used as module, but that's another story (naming is hard, and this whole module story is not scope of this gist).
The "$@" in shell, as well as in bash, would properly expand any optional extra arguments passed to the initial, shell, program.
Coincidentally with the end of this gist, the last part of the technique ;exit $? simply split in two different commands the previous node execution, and the exiting of the initial shell program itself, so that shell won't parse any extra content and it will exit passing along the same exit code node produced.
This is thanks to the $? special shell, and bash, variable.
@WebReflection , this is awesome, thousand thanks for this! 🙏 💯
However, I can't seem to pass a param. The "$@" doesn't work and is instead treating params as if I was requiring/running other scripts.
EDIT, Workaround:
I was able to pass a parameter by adding a string substitution instead. and seeing that "$@" doesn't seem to work I removed it:
then added an undefined variable in my esm script, $notestitle.