Skip to content

Instantly share code, notes, and snippets.

@WebReflection
Last active March 4, 2024 20:55
Show Gist options
  • Select an option

  • Save WebReflection/8840ec29d296f2fa98d8be0102f08590 to your computer and use it in GitHub Desktop.

Select an option

Save WebReflection/8840ec29d296f2fa98d8be0102f08590 to your computer and use it in GitHub Desktop.

Revisions

  1. WebReflection revised this gist Jan 12, 2021. 1 changed file with 12 additions and 0 deletions.
    12 changes: 12 additions & 0 deletions executable-standalone-module.md
    Original file line number Diff line number Diff line change
    @@ -160,4 +160,16 @@ $ echo $?
    ```

    ### Alternative: enforced ESM

    This version ensures if there's no `package.json` in the folder it puts one with `{"type":"module"}` content.
    ```sh
    #!/usr/bin/env sh
    J="$(dirname $0)/package.json"//;if [ ! -f "${J:0:-2}" ];then echo '{"type":"module"}'>"${J:0:-2}";fi;echo -e "\n\n$(sed "1,2d" "$0")"|node --input-type=module "$@";exit $?
    import { version } from 'process';
    console.log(`Running Node ${version} in ESM mode!`);
    ```
  2. WebReflection revised this gist Apr 1, 2020. 1 changed file with 21 additions and 0 deletions.
    21 changes: 21 additions & 0 deletions executable-standalone-module.md
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,24 @@
    ## Update

    If you're OK in having a `node-esm` executable, please consider [this solution](https://github.com/nodejs/node/issues/32316#issuecomment-606954468).

    ```sh
    #!/usr/bin/env sh
    # the /usr/local/bin/node-esm executable
    input_file=$1
    shift
    exec node --input-type=module - $@ <$input_file
    ```

    Example:
    ```js
    #!/usr/bin/env node-esm
    import { version } from 'process';
    console.log(version);
    ```

    - - -

    ### About

    In [this tweet](https://twitter.com/WebReflection/status/1116757433664192512) 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`.
  3. WebReflection revised this gist Apr 23, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion executable-standalone-module.md
    Original file line number Diff line number Diff line change
    @@ -110,7 +110,7 @@ The `"$@"` in shell, as well as in bash, would properly expand any optional extr

    #### Are imports ESM too ?

    Please note that, even if executed sa ESM, the program won't act as if there was a `"type": "module"` field within a `package.json` in the same, or upper, directories.
    Please note that, even if executed as ESM, the program won't act as if there was a `"type": "module"` field within a `package.json` in the same, or upper, directories.

    However, if you import globally installed modules, you don't need to worry about it, while if you'll import local files, you either need to have a `package.json` file with `"type":"module"` field in the same folder, or in one of its parent folders.

  4. WebReflection revised this gist Apr 23, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion executable-standalone-module.md
    Original file line number Diff line number Diff line change
    @@ -15,7 +15,7 @@ Special thanks to [Geoffrey Booth](https://twitter.com/geoffreybooth) for hints,
    #!/usr/bin/env sh
    J=S//;echo "\n\n$(sed "1,2d" "$0")"|node --input-type=module "$@";exit $?
    /* your 100% ESM code from this line on */
    console.log('ESM');
    console.log(import.meta);
    ```
    ## Compatibility
  5. WebReflection revised this gist Apr 14, 2019. 1 changed file with 17 additions and 3 deletions.
    20 changes: 17 additions & 3 deletions executable-standalone-module.md
    Original file line number Diff line number Diff line change
    @@ -24,6 +24,7 @@ console.log('ESM');
    * most unix and Linux based distribution (please LMK if one is incompatible, thanks)
    * Windows WSL, and every other way to have a bash, or shell, environment on Windows


    ## The First line

    It's a regular [hashbang](https://en.wikipedia.org/wiki/Shebang_(Unix)), 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`.
    @@ -92,11 +93,11 @@ echo "\n\n$(sed ...)" | node ...

    ### The node execution

    [After long discussions](https://github.com/nodejs/modules/issues/300#issuecomment-477728981), it became obvious that the previously proposed flag `--entry-type=module` would've caused more confusion than expected, because it wasn't the equivalent to a `"type": "module"` field within a `package.json`.
    The upcoming version of _node_, and as part of its `–experimental-modules` flag switch, will make possible to execute any sort of _stdin_ as pure ESM, instead of _CommonJS_.

    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 cases via `-e`, or _stdin_.
    This is particularly handy for evaluation cases, or input produced by 3rd parts software, without the need to write intermediate files on the disk, as it is in this very same case.

    But fear not, this technique perfectly addresses that _stdin_ case, making the file itself, the source of the _ESM_ code that will be executed.
    This technique perfectly addresses indeed the _stdin_ execution case, making the file itself, the source of the _ESM_ that will be executed.

    ```sh
    node --input-type=module "$@"
    @@ -107,6 +108,19 @@ Above shell or bash command, switches _node_ input parsing goal as **module**, w
    The `"$@"` in shell, as well as in bash, would properly expand any optional extra arguments passed to the initial, shell, program.


    #### Are imports ESM too ?

    Please note that, even if executed sa ESM, the program won't act as if there was a `"type": "module"` field within a `package.json` in the same, or upper, directories.

    However, if you import globally installed modules, you don't need to worry about it, while if you'll import local files, you either need to have a `package.json` file with `"type":"module"` field in the same folder, or in one of its parent folders.


    #### How to have all imports as ESM
    If you want to always threat any imported NodeJS content as ESM, you can `echo '{"type":"module"}'>>~/package.json` so that the top most folder, per the current user, reached by _npm_, will understand the default import parsing goal of any file.

    To opt out, you can always explicitly import a `.cjs` file, so that legacy compatibility is preserved.


    ### The exit

    Inspired by [this suggestion](https://unix.stackexchange.com/questions/182303/creating-a-shebang-pointing-portably-to-an-interpreter-in-the-folder-of-a-script/182304#182304), and coincidentally with the end of this gist, the last part of the technique `;exit $?` simply splits, 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.
  6. WebReflection revised this gist Apr 14, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion executable-standalone-module.md
    Original file line number Diff line number Diff line change
    @@ -54,7 +54,7 @@ The reason _echo_ is part of this technique, is that we'd like to maintain the l

    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 _sed_.
    It's important to use doublequotes to concatenate two new lines, `"\n\n"` and the consumed _sed_ output.

    In shell, as well as in bash, double quotes can contain the output produced by system calls, as it is in the case of `echo "1$(echo 2)3"`, which will simply output `123`.

  7. WebReflection revised this gist Apr 14, 2019. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions executable-standalone-module.md
    Original file line number Diff line number Diff line change
    @@ -6,6 +6,8 @@ The technique would work for files without an extension, like `myprogram`, as we

    This gist is an attempt to describe the technique I've used.

    Special thanks to [Geoffrey Booth](https://twitter.com/geoffreybooth) for hints, suggestions, and review of this content.


    # TL;DR - The Solution

  8. WebReflection revised this gist Apr 14, 2019. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions executable-standalone-module.md
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,5 @@
    ### About

    In [this tweet](https://twitter.com/WebReflection/status/1116757433664192512) 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`.

    The technique would work for files without an extension, like `myprogram`, as well as files with any extension, including `myprogram.js`, `myprogram.mjs`, `myprogram.exe`, or `myprogram.iscool`.
  9. WebReflection revised this gist Apr 14, 2019. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions executable-standalone-module.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,7 @@
    In [this tweet](https://twitter.com/WebReflection/status/1116757433664192512) 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`.

    The technique would work for files without an extension, like `myprogram`, as well as files with any extension, including `myprogram.js`, `myprogram.mjs`, `myprogram.exe`, or `myprogram.iscool`.

    This gist is an attempt to describe the technique I've used.


  10. WebReflection revised this gist Apr 14, 2019. 1 changed file with 6 additions and 0 deletions.
    6 changes: 6 additions & 0 deletions executable-standalone-module.md
    Original file line number Diff line number Diff line change
    @@ -12,6 +12,12 @@ J=S//;echo "\n\n$(sed "1,2d" "$0")"|node --input-type=module "$@";exit $?
    console.log('ESM');
    ```

    ## Compatibility

    * OSX and macOS
    * most unix and Linux based distribution (please LMK if one is incompatible, thanks)
    * Windows WSL, and every other way to have a bash, or shell, environment on Windows

    ## The First line

    It's a regular [hashbang](https://en.wikipedia.org/wiki/Shebang_(Unix)), 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`.
  11. WebReflection revised this gist Apr 14, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion executable-standalone-module.md
    Original file line number Diff line number Diff line change
    @@ -64,7 +64,7 @@ In shell, as well as in bash, the `$0` always refers to the file that is current

    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 _echo_ output, the file without the first two lines, to the _echo_ command.
    In this example, the pipe passes the _echo_ output, including two `\n` and the output produced by sed, as _node_ _stdin_.


    ### To Recap
  12. WebReflection revised this gist Apr 14, 2019. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions executable-standalone-module.md
    Original file line number Diff line number Diff line change
    @@ -21,6 +21,8 @@ This first line requires that the file is executable, something easy to obtain v

    ## The Second Line

    This part was inspired by [cgjs executable](https://github.com/cgjs/cgjs/blob/master/packages/cgjs/cg.js#L1-L2).

    Technically speaking, the `J=S//;` part of the string could be omitted, but then any JavaScript editor would show errors due usage of incompatible [shell](https://en.wikipedia.org/wiki/Unix_shell) syntax.

    Using `J=S//;` as prefix instead, would simply assign, in the shell world, as well as in the bash one, the string `S//` to a variable `J`, but it won't trigger errors, neither it will ever execute in the NodeJS world (no global `J` leak whatsoever).
  13. WebReflection revised this gist Apr 14, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion executable-standalone-module.md
    Original file line number Diff line number Diff line change
    @@ -95,7 +95,7 @@ The `"$@"` in shell, as well as in bash, would properly expand any optional extr

    ### The exit

    Coincidentally with the end of this gist, the last part of the technique `;exit $?` simply splits, 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.
    Inspired by [this suggestion](https://unix.stackexchange.com/questions/182303/creating-a-shebang-pointing-portably-to-an-interpreter-in-the-folder-of-a-script/182304#182304), and coincidentally with the end of this gist, the last part of the technique `;exit $?` simply splits, 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, which simply contains the last _exit_ code.

  14. WebReflection revised this gist Apr 14, 2019. 1 changed file with 19 additions and 27 deletions.
    46 changes: 19 additions & 27 deletions executable-standalone-module.md
    Original file line number Diff line number Diff line change
    @@ -32,6 +32,19 @@ In shell world, as well as in the bash one, it will simply keep executing whatev
    Keep in mind, shell scripts, as well as bash scripts, execute as a stream, instead of requiring upfront parsing and syntax validation, meaning that exiting a file at any time won't cause syntax errors, even if the content after exiting is invalid.


    ### The echo command

    The [echo](https://en.wikipedia.org/wiki/Echo_(command)) 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 _sed_.

    In shell, as well as in bash, double quotes can contain the output produced by system calls, as it is in the case of `echo "1$(echo 2)3"`, which will simply output `123`.


    ### The sed part

    The micro utility [sed](https://en.wikipedia.org/wiki/Sed) is a quite universal way to replace, hence transform, some content.
    @@ -49,38 +62,17 @@ In shell, as well as in bash, the `$0` always refers to the file that is current

    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

    The [echo](https://en.wikipedia.org/wiki/Echo_(command)) 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 well as in bash, double quotes can contain the output produced by system calls, as it is in the case of `echo "1$(echo 2)3"`, which will simply output `123`.


    ### The cat utility

    Similarly to _sed_, [cat](https://en.wikipedia.org/wiki/Cat_(Unix)) is the most common way to stream some content to the standard output.
    In this example, the pipe passes the _echo_ output, the file without the first two lines, to the _echo_ command.

    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](https://en.wikipedia.org/wiki/Less_(Unix)), as example, so that you could read, through the terminal, even big files by typing `cat filename | less`.

    In this standalone specific case, `cat` is used because of its handy `-` feature, that simply passes through whatever input the current program is receiving.
    ### To Recap

    To recap, `echo "\n\n$(cat -)"` will pass along an output containing two new lines `\n` plus whatever _sed_ outputted before.
    The `echo "\n\n$(sed "1,2d" "$0")"` will pass along an output containing two new lines `\n` plus whatever _sed_ outputted before.

    ```sh
    ┏━━━━━━━━━━━━┳━━━━▶
    sed ... | echo "\n\n$(cat -)" | ...
    ┗━━┳━━┛ ┗━━━┳━━┛
    ┃ ┃
    ┗━━ output as input ━┛
    as node stdin
    ┏━━━━━━━━━━━━━━┳━━▶
    echo "\n\n$(sed ...)" | node ...
    ```


  15. WebReflection revised this gist Apr 14, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion executable-standalone-module.md
    Original file line number Diff line number Diff line change
    @@ -7,7 +7,7 @@ This gist is an attempt to describe the technique I've used.

    ```js
    #!/usr/bin/env sh
    J=S//;sed "1,2d" "$0"|echo "\n\n$(cat -)"|node --input-type=module "$@";exit $?
    J=S//;echo "\n\n$(sed "1,2d" "$0")"|node --input-type=module "$@";exit $?
    /* your 100% ESM code from this line on */
    console.log('ESM');
    ```
  16. WebReflection renamed this gist Apr 14, 2019. 1 changed file with 0 additions and 0 deletions.
  17. WebReflection renamed this gist Apr 13, 2019. 1 changed file with 0 additions and 0 deletions.
  18. WebReflection revised this gist Apr 13, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion node-standalone-exacutable.md
    Original file line number Diff line number Diff line change
    @@ -23,7 +23,7 @@ This first line requires that the file is executable, something easy to obtain v

    Technically speaking, the `J=S//;` part of the string could be omitted, but then any JavaScript editor would show errors due usage of incompatible [shell](https://en.wikipedia.org/wiki/Unix_shell) syntax.

    Using `J=S//;` as prefix instead, would simply assign, in the shell world, as well as in the bash one, the string `S//` to a variable `J`, but it won't trigger errors, neither it will ever execute in the NodeJS world.
    Using `J=S//;` as prefix instead, would simply assign, in the shell world, as well as in the bash one, the string `S//` to a variable `J`, but it won't trigger errors, neither it will ever execute in the NodeJS world (no global `J` leak whatsoever).

    However, it will start an inline comment, for your IDE of choice, so that no errors would be shown while editing such file.

  19. WebReflection revised this gist Apr 13, 2019. 1 changed file with 40 additions and 19 deletions.
    59 changes: 40 additions & 19 deletions node-standalone-exacutable.md
    Original file line number Diff line number Diff line change
    @@ -8,27 +8,28 @@ This gist is an attempt to describe the technique I've used.
    ```js
    #!/usr/bin/env sh
    J=S//;sed "1,2d" "$0"|echo "\n\n$(cat -)"|node --input-type=module "$@";exit $?
    /* your 100% ESM code here */
    /* your 100% ESM code from this line on */
    console.log('ESM');
    ```

    ## The First line

    It's a regular [hashbang](https://en.wikipedia.org/wiki/Shebang_(Unix)), 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`.
    This first line requires that the file is executable, something easy to obtain via [chmod](https://en.wikipedia.org/wiki/Chmod), as in `chmod +x filename`.


    ## The Second Line

    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.
    Technically speaking, the `J=S//;` part of the string could be omitted, but then any JavaScript editor would show errors due usage of incompatible [shell](https://en.wikipedia.org/wiki/Unix_shell) syntax.

    Using `J=S//;` as prefix instead, would simply assign, in the [shell](https://en.wikipedia.org/wiki/Unix_shell) world, the string `S//` to a variable `J`, but it won't trigger errors, neither it will ever execute, in the NodeJS world.
    Using `J=S//;` as prefix instead, would simply assign, in the shell world, as well as in the bash one, 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.
    However, it will start an inline comment, for your IDE of choice, so that no errors would be shown while editing such file.

    In shell world, it will simply keep executing whatever is after the semicolon.
    In shell world, as well as in the bash one, 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.
    Keep in mind, shell scripts, as well as bash scripts, execute as a stream, instead of requiring upfront parsing and syntax validation, meaning that exiting a file at any time won't cause syntax errors, even if the content after exiting is invalid.


    ### The sed part
    @@ -41,7 +42,7 @@ 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 well as in bash, the `$0` always refers to the file that is currently executing, like `__filename` would be in _CommonJS_.


    ### The pipe
    @@ -55,47 +56,67 @@ In this example, the pipe passes the _sed_ output, the file without the first tw

    The [echo](https://en.wikipedia.org/wiki/Echo_(command)) 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.
    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`.
    In shell, as well as in bash, double quotes can contain the output produced by system calls, as it is in the case of `echo "1$(echo 2)3"`, which will simply output `123`.


    ### The cat utility

    Similarly to _sed_, [cat](https://en.wikipedia.org/wiki/Cat_(Unix)) is the most common way to stream some content to the standard outoput.
    Similarly to _sed_, [cat](https://en.wikipedia.org/wiki/Cat_(Unix)) is the most common way to stream some content to the standard output.

    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](https://en.wikipedia.org/wiki/Less_(Unix)), as example, so that you can read through the console even big files via `cat filename | less`.
    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](https://en.wikipedia.org/wiki/Less_(Unix)), as example, so that you could read, through the terminal, even big files by typing `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.
    In this standalone specific case, `cat` is used because of its handy `-` feature, that simply passes through whatever input the current program is receiving.

    To recap, `echo "\n\n$(cat -)"` will pass along an output containing two new lines `\n` plus whatever _sed_ outputted before.

    ```sh
    ┏━━━━━━━━━━━━┳━━━━▶
    sed ... | echo "\n\n$(cat -)" | ...
    ┗━━┳━━┛ ┗━━━┳━━┛
    ┃ ┃
    ┗━━ output as input ━┛
    ```


    ### The node execution

    [After long discussions](https://github.com/nodejs/modules/issues/300#issuecomment-477728981), 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`.
    [After long discussions](https://github.com/nodejs/modules/issues/300#issuecomment-477728981), it became obvious that the previously proposed flag `--entry-type=module` would've caused more confusion than expected, because it wasn't the equivalent to a `"type": "module"` field within 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.
    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 cases via `-e`, or _stdin_.

    But fear not, this technique perfectly addresses that evaluation case, making the file itself, the source of the _ESM_ code that will be evaluated.
    But fear not, this technique perfectly addresses that _stdin_ case, making the file itself, the source of the _ESM_ code that will be executed.

    ```sh
    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).
    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 <sup><sub>(naming is hard, and this whole module story is not scope of this gist)</sub></sup>.

    The `"$@"` in shell, as well as in bash, would properly expand any optional extra arguments passed to the initial, shell, program.


    ### The exit

    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.
    Coincidentally with the end of this gist, the last part of the technique `;exit $?` simply splits, 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.
    This is thanks to the `$?` special shell, and bash, variable, which simply contains the last _exit_ code.

    ```sh
    $ echo $?
    0

    $ trigger error
    -bash: trigger: command not found

    $ echo $?
    127

    ```


  20. WebReflection revised this gist Apr 13, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion node-standalone-exacutable.md
    Original file line number Diff line number Diff line change
    @@ -15,7 +15,7 @@ J=S//;sed "1,2d" "$0"|echo "\n\n$(cat -)"|node --input-type=module "$@";exit $?

    It's a regular [hashbang](https://en.wikipedia.org/wiki/Shebang_(Unix)), 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 chomd, as in `chomd +x filename`.
    This first line requires that the file is executable, something easy to obtain via chmod, as in `chmod +x filename`.


    ## The Second Line
  21. WebReflection created this gist Apr 13, 2019.
    101 changes: 101 additions & 0 deletions node-standalone-exacutable.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,101 @@
    In [this tweet](https://twitter.com/WebReflection/status/1116757433664192512) 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.


    # TL;DR - The Solution

    ```js
    #!/usr/bin/env sh
    J=S//;sed "1,2d" "$0"|echo "\n\n$(cat -)"|node --input-type=module "$@";exit $?
    /* your 100% ESM code here */
    ```

    ## The First line

    It's a regular [hashbang](https://en.wikipedia.org/wiki/Shebang_(Unix)), 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 chomd, as in `chomd +x filename`.


    ## The Second Line

    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](https://en.wikipedia.org/wiki/Unix_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 sed part

    The micro utility [sed](https://en.wikipedia.org/wiki/Sed) is a quite universal way to replace, hence transform, some content.

    ```sh
    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.


    ### The pipe

    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

    The [echo](https://en.wikipedia.org/wiki/Echo_(command)) 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`.


    ### The cat utility

    Similarly to _sed_, [cat](https://en.wikipedia.org/wiki/Cat_(Unix)) 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](https://en.wikipedia.org/wiki/Less_(Unix)), 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.


    ### The node execution

    [After long discussions](https://github.com/nodejs/modules/issues/300#issuecomment-477728981), 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.

    ```sh
    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.


    ### The exit

    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.