1.0.1 • Published 8 months ago

esm-executable v1.0.1

Weekly downloads
-
License
MIT
Repository
-
Last release
8 months ago

creating shell scripts with node/esm

intro

Learn how to create an executable standalone script two different ways:

  • node/esm
  • npm package + node/esm

The finished package is available here:

  • On GitHub as @frankg/demo
  • On npm as @frankg/demo

node/esm as a standalone shell script

FILE EXTENSIONS

  • .mjs interpreted as esm
  • .js interpreted as esm when you include "type": "module" in package.json

Since we're creating a standalone script we don't have package.json we use .mjs

import * as os from "node:os";

const { username } = os.userInfo();
console.log(`Hello ${username}!`);

RUNNING WITH NODE

  • node hello.mjs

RUNNING STANDALONE

  • edit script
#!/usr/bin/env node
import * as os from "node:os";

const { username } = os.userInfo();
console.log(`Hello ${username}!`);
  • make it executable -- chmod u+x hello.mjs
  • run it ./hello.mjs

node/esm as an npm package

creating the files

  • create directory mkdir demo; cd demo
  • create package.json file npm init -y
  • add dependencies pn add lodash-es -- creates node_modules folder and package-lock.json file
  • add readme.md file
  • add add two shell scripts
    demo/
        package.json
        package-lock.json
        readme.md
        src/
            homedir.mjs
            versions.mjs
  • add content to package.json
  ...
  "type": "module",
  "bin": {
    "homedir": "./src/homedir.mjs",
    "versions": "./src/versions.mjs"
  },
  ...
  • package.json contents -- confirm required publishing fields: name, version, license
{
  "name": "esm-executable",
  "version": "1.0.0",
  "description": "## intro",
  "main": "index.js",
  "type": "module",
  "bin": {
    "homedir": "./src/homedir.mjs",
    "versions": "./src/versions.mjs"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "MIT",
  "dependencies": {
    "lodash-es": "^4.17.21"
  }
}
  • populate homedir.mjs -- you can run with node ./src/homedir.mjs
#!/usr/bin/env node
import { homedir } from "node:os";

console.log("Homedir: " + homedir());
  • populate versions.mjs -- you can run with node ./src/versions.mjs
#!/usr/bin/env node

import { pick } from "lodash-es";

console.log(pick(process.versions, ["node", "v8", "unicode"]));

how npm runs shell scripts

INSTALLING ON UNIX

A script such as homedir.mjs does not need to be executable on Unix because npm installs it via an executable symbolic link:

  • when installing the package globally, the link is added to a directory that’s listed in $PATH.
  • when installing the package locally (as a dependency), the link is added to node_modules/.bin/

publishing the package

Before we publish lets check the configuration

  • .gitignore is honored
  • .npmignore overides .gitignore
  • see files excluded by default:

exclude by default

node_modules
.*.swp
._*
.DS_Store
.git
.gitignore
.npmignore
.npmrc
npm-debug.log

never exclude

package.json
README.md and its variants
CHANGELOG and its variants
LICENSE, LICENCE

Here are some things we can check

  • check which files will be uploaded npm publish --dry-run
  • create an archive package as it's store on the registry npm pack
  • install globally with either of these two: npm link or npm install . -g
  • list globally installed packages npm ls -g -- see the package-name
  • create an npm account if needed
  • upload to the registry npm publish --access public - access only needed first time
  • change access level if needed
  • a new version is required for every upload -- use semver - major.minor.patch

avoiding the .mjs extension

Here's a magic trick to avoid having to use the .mjs extension throught your code.

  • prepend your shell script files with the following snippet:
#!/bin/sh
":"; // ; cat "$0" | node --input-type=module - $@ ; exit $?

UPDATE: I took out the snippet and it still works with this

#!/usr/bin/env node
# see the package installed
npm ls -g

...
├── esm-executable@1.0.1 -> ./../../../../../dev/code/scratch/typescript/esm-executable
...

# find the node path
which node

/Users/frankg/.nvm/versions/node/v18.15.0/bin/node


# show the executable shell scripts
ls /Users/frankg/.nvm/versions/node/v18.15.0/bin

lrwxr-xr-x  1 frankg  staff    49B Sep 11 03:53 homedir -> ../lib/node_modules/esm-executable/src/homedir.js
lrwxr-xr-x  1 frankg  staff    50B Sep 11 03:53 versions -> ../lib/node_modules/esm-executable/src/versions.js
1.0.1

8 months ago

1.0.0

8 months ago