{"id":13902720,"url":"https://github.com/bradymholt/jsh","last_synced_at":"2025-04-13T06:35:25.931Z","repository":{"id":37084175,"uuid":"504343142","full_name":"bradymholt/jsh","owner":"bradymholt","description":"Helpers for Bash like shell scripting in JavaScript","archived":false,"fork":false,"pushed_at":"2024-11-21T19:02:01.000Z","size":459,"stargazers_count":34,"open_issues_count":0,"forks_count":3,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-03-24T04:22:28.611Z","etag":null,"topics":["bash","bash-script","javascript","shell","shell-script","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bradymholt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["bradymholt"]}},"created_at":"2022-06-17T00:20:12.000Z","updated_at":"2025-01-14T22:14:30.000Z","dependencies_parsed_at":"2024-09-25T01:41:53.237Z","dependency_job_id":"81adc79e-0471-4428-a0db-ed142c5a1cd1","html_url":"https://github.com/bradymholt/jsh","commit_stats":{"total_commits":264,"total_committers":2,"mean_commits":132.0,"dds":"0.23106060606060608","last_synced_commit":"250534a3033e67246cae153178952f52e9ba5a5b"},"previous_names":[],"tags_count":48,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bradymholt%2Fjsh","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bradymholt%2Fjsh/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bradymholt%2Fjsh/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bradymholt%2Fjsh/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bradymholt","download_url":"https://codeload.github.com/bradymholt/jsh/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248675122,"owners_count":21143762,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bash","bash-script","javascript","shell","shell-script","typescript"],"created_at":"2024-08-06T22:01:20.609Z","updated_at":"2025-04-13T06:35:25.901Z","avatar_url":"https://github.com/bradymholt.png","language":"TypeScript","funding_links":["https://github.com/sponsors/bradymholt"],"categories":["bash"],"sub_categories":[],"readme":"# jsh [![Build](https://github.com/bradymholt/jsh/actions/workflows/build.yml/badge.svg)](https://github.com/bradymholt/jsh/actions/workflows/build.yml) [![NPM Package](https://img.shields.io/npm/v/jsh.svg)](https://www.npmjs.com/package/jsh)\n\n\n\u003cimg align=\"left\" src=\"https://user-images.githubusercontent.com/759811/210273710-b13913e2-0a71-4d9d-94da-1fe538b8a73e.gif\"/\u003e\n\n\u003cbr/\u003e\n\n \u0026nbsp;**Would you take a quick second and ⭐️ my repo?**\n\n\u003cbr/\u003e\n\n\nHelpers for Bash like shell scripting in JavaScript\n\n\u003cp align=\"left\"\u003e\n\u003cimg alt=\"jsh logo\" src=\"https://user-images.githubusercontent.com/759811/174220014-2dcb1c9b-17b4-4a61-8f40-ddfa2d1e7c7f.png\"\u003e\n\u003c/p\u003e\n\njsh, pronounced \"j shell\", is a small JavaScript library (~20KB and no dependencies!) that provides helper aliases and functions that are similar to Bash syntax, allowing you to write shell scripts in JavaScript / Node.js that are simple and familiar.\n\n**Requirement**: Node.js \u003e=16\n\n## Quick Start\n\nCreate a file called `script.js`:\n\n```js\n#!/usr/bin/env -S npx jsh\n\necho(\"Hello jsh!\")\n```\n\nMake the file executable, run it, and you should see \"Hello jsh\" printed:\n\n```shell\nchmod +x ./script.js \u0026\u0026 ./script.js\n\n\u003e Hello jsh!\n```\n\nSee [detailed installation instructions below](#installation).\n\n## Helpers\n\nBelow is a list of the available helpers.  You can refer to the [declaration file](https://github.com/bradymholt/jsh/releases/latest/download/index.d.ts) for a full list of the helpers and JSDoc documentation for arguments and usage.\n\nAll helpers listed below (except for the HTTP Requests helpers) are **synchronous** functions and will block while running.\n\n**General Scripting**\n|     | Description |\n| --- | --- |\n| `echo(\"Hello\")` | Print text to console with trailing newline |\n| `echo.yellow(\"Hello\")`,\u003cbr/\u003e `echo.green(\"Hello\")`,\u003cbr/\u003e`echo.red(\"Hello\")`,\u003cbr/\u003e`echo.blue(\"Hello\")` | Prints colored text to console with trailing newline |\n| `printf(\"Processing...\")` | Print text to console without a trailing newline |\n| `exit(1)` | Halt the script and return an exit code |\n| `error(\"An error\", 1)` | Echo an error and halt the script with an exit code |\n| `const name = prompt(\"What is your name?\");` | Prompt for user input and return after \\\u003cEnter\\\u003e pressed; also aliased as `read()`. |\n| `sleep(2000)` | Sleep for specified number of milliseconds (synchronous and will not block event loop) |\n\n**Command Execution** ([detailed docs below](#command-execution-helpers))\n|     | Description |\n| --- | --- |\n| `result=$(\"cmd.sh\")` | Execute a command and return the stdout |\n| `exec(\"cmd.sh\")` | Execute a command and stream stdout to console without returning a value |\n\n**Arguments and Environment**\n|     | Description |\n| --- | --- |\n| `$0` | Return the name of the **entry** script file (ex: `my_script.js`) |\n| `$1, $2, $3` or\u003cbr/\u003e `args[0], args[1], args[2]` ... | Access arguments that have been passed |\n| `args.source_file, args.v` | Access arguments prefixed with \"--\" or \"-\".\u003cbr/\u003e\u003cbr/\u003eIf argument is in format `--source_file=input.txt` the value of `args.source_file` would be `\"input.txt\"`.\u003cbr/\u003e\u003cbr/\u003eIf argument is in format `--source_file` or `-v` the argument name will be available on args as a `true` boolean (`args.source_file == true`, `args.v == true`)   |\n| `const [source_file, target_file] = args.assertCount(2)` | Return arg values as array or call `usage.printAndExit()` if less than number of arguments specified were supplied.  See [details below](#usage-helpers). |\n| `$HOME`,`env.HOME`, or `env[\"HOME\"]`| Access an environment variable |\n| `$PWD`,`env.PWD`| The current working directory |\n| `const USER = env.assert(\"USER\")`or\u003cbr/\u003e`const [HOME, USER] = env.assert([\"HOME\", \"USER\"])` | Return environment variable value or call `usage.printAndExit()` if undefined.  You can also pass an array of environment variable names and an array of values will be returned.  See [details below](#usage-helpers).  |\n| `stdin()` | Read stdin as a string |\n| `usage(\"Usage: myscript.js [--verbose]\")` | Define a usage message.  See [details below](#usage-helpers). |\n| `usage.printAndExit()` | Print the usage message and then exit with an error exit code.  If `usage()` was not previously called to define a usage message, a default one will be used.  See [details below](#usage-helpers). |\n\n**File System**\n|     | Description |\n| --- | --- |\n| `cd(\"/usr/bin\")` | Change the current working directory |\n| `config=cat(\"cnf.txt\")` | Read text from file; also aliased as `readFile()`. |\n| `writeFile(\"cnf.txt\", \"World\")` | Write text to file |\n| `ls(\"./myDir\")` | Return absolute paths for _files_ in a directory (recursive by default) |\n| `dirExists(\"./myDir\")` | Check if directory exists |\n| `mkdir(\"./newDirName\")` | Create a directory (recursive by default) |\n| `rmdir(\"./newDirName\")` | Delete a directory (recursive by default) |\n| `exists(\"./aFile.txt\")` | Check if a file exists |\n| `rm(\"./myFile\")` | Delete a file |\n| `dirName(\"./path/file.txt\")` | Return the directory name for a path |\n| `__dirname` | Returns the absolute path (directory) containing the **entry** script (works inside of CommonJS or ES module) |\n| `__filename` | Returns the name of the **entry** script (works inside of CommonJS or ES module) |\n\n**HTTP Requests** ([detailed docs below](#http-request-helpers))\n\n**Note:** The HTTP helper functions are **asynchronous** and return a Promise.\n\n|     | Description |\n| ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |\n| `await http.get(\"https://www.myapi.com\")`                   | Make a HTTP GET request and return the response body data                                                |\n| `await http.post(\"https://www.myapi.com\", { data: \"1\" }, { Authorization: \"Bearer abc123\"})`   | Make a HTTP POST request and return the response body data                                               |\n| `await http.put(\"https://www.myapi.com\", { data: \"1\" })`    | Make a HTTP PUT request and return the response body data                                                |\n| `await http.patch(\"https://www.myapi.com\", { data: \"1\" })`  | Make a HTTP PATCH request and return the response body data                                              |\n| `await http.delete(\"https://www.myapi.com\", { data: \"1\" })` | Make a HTTP DELETE request and return the response body data                                             |\n| `await http(\"POST\", \"https://www.myapi.com\", { data: \"1\" }, { headers: { Accept: \"application/json\" } })`                | Make a HTTP request and return the response: (`{ data, headers, statusCode, statusMessage }`)            |\n| `await http.upload(\"https://www.myapi.com/logo\", \"./logo.jpg\", \"image/jpg\")` | Upload a file with a HTTP POST request |\n| `await http.download(\"https://www.myapp.com/logo.jpg\", \"./logo.jpg\")` | Perform a HTTP GET request and save the response to a local file path |\n\n\n## Examples\n\n\u003cdetails\u003e\n\u003csummary\u003eWrite text to file\u003c/summary\u003e\n\n```js\n#!/usr/bin/env npx jsh\n\nusage(`\\\nUsage:\n  ${$0} text target_file [--verbose]\n\nExample:\n  ${$0} \"My text\" ./test.txt --verbose\n\nWrites some text to a file\\\n`);\n\nconst [text, target_file] = args.assertCount(2);\n\nif (args.verbose) echo(`Writing text to file...`);\n\nwriteFile(target_file, text);\n\nif (args.verbose) echo.green(`Done!`);\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003ePrompt for input\u003c/summary\u003e\n\n```js\n#!/usr/bin/env npx jsh\n\nusage(`\\\nUsage:\n  ${$0} prompt_text\n\nExample:\n  ${$0} \"What is your name?\"\n\nPrompts for input and then echos it\\\n`);\n\nconst input = prompt($1); // `$1` contains the first argument which is prompt_text\necho(input);\n```\n\u003c/details\u003e\n\n## Command Execution Helpers\n\n### $()\n\nWhen you want to run a command and buffer the output (stdout) of that command as a return value, you use the synchronous function **$()**. As the command is running, stdout will _not_ be printed to the console but will instead be captured and returned as the result. This helper is intended for short running commands that do not produce a large amount of output.\n\nExample:\n\n```js\n// Will wait for `git status` to complete and assign output to `result` variable.\n// Nothing will be printed to the console.\n\nlet result=$(`git status --porcelain`);\n```\n\n### exec\n\n**exec** should be used when running commands where the output (stdout) does not need to be captured, but only printed to the console. This helper is intended for long running commands or those where output does not need to be captured.\n\nExample:\n\n```js\n// Will print `npm install` output immediately as it happens\n// exec will not return anything (void)\n\nexec(`npm install`)\n\n\u003e added 379 packages, and audited 380 packages in 1s\n\u003e 29 packages are looking for funding\n\u003e ...\n```\n\n### Error Handling\n\nIf a command exits with a non-zero status, a `CommandError` error will be thrown. The error contains these properties: `{ message, command, stdout, stderr, status }`.\n\nExample:\n\n```js\ntry {\n  const output = $(`cat invalid.txt`)\n} catch (err) {\n  echo(err.message) // Error running command: `cat invalid.txt`\n  echo(err.command) // cat invalid.txt\n  echo(err.stderr) // \"cat: invalid.txt: No such file or directory\"\n  echo(err.status) // 1\n}\n```\n\n#### `noThrow` option\n\nYou can pass in the option `noThrow: true` to prevent an error from being thrown. Instead, the stderr (or stdout) will be returned.\n\nExample:\n\n```js\n// This command will error out but will not throw because `$.noThrow()` was called.\nlet content=$(`cat invalid.txt`, { noThrow: true})\necho(content);\n\n\u003e cat: invalid.txt: No such file or directory\n```\n\n### Command Options\n\n`$()` and `exec()` accept an `options` parameter object that may contain any of the following fields:\n \n- `echoCommand: boolean` - If true will echo the command itself before running it (Default: `true` for `exec()` and `false` for `$()`)\n- `noThrow: boolean` -  If set to true, will not throw if the command returns a non-zero exit code (Default: `false`)\n- `timeout: number` - In milliseconds the maximum amount of time the process is allowed to run (Default: `undefined` (unlimited))\n- `shell: string` - By default, commands will be run inside of a shell (`/bin/sh` on *nix systems and `process.env.ComSpec` on Windows).  This option can be used to specify the path to a different shell to execute commands with.  For example, you could specify `shell: \"/bin/bash\"` to use bash.\n- `maxBuffer: number` - Specifies the largest number of bytes allowed on stdout or stderr. If this value is exceeded, the child process will be terminated. (Default: `268435456` (256MB))\n\n\n\n## Usage Helpers\n\nTo define usage instructions for your script, you can call `usage()` and pass in a usage string that describes the script and documents any required or optional arguments.  \n\nExample:\n```js\nusage(`\\\nUsage:\n  json_formatter.js source_file target_file [--verbose]\n\nExample:\n  json_formatter.js ./my_in_file.json ./my_out_file.json --verbose\n\nFormats a JSON file\n`);\n```\n\nYou can also use `$0` to reference the name of the current script rather than having to hardcode it.  The above example could be changed to (`${$0} source_file target_file [--verbose]` ...).\n\n### usage.printAndExit()\n\nYou can call `usage.printAndExit()` at any time to print the usage instructions and then immediately exit with an error code.  If you call usage.printAndExit() _before_ calling usage(), a simple default message will be echoed but if you call usage.printAndExit() _after_ calling usage(), your custom usage instructions will be echoed.\n\nThere are a few ways that usage.printAndExit() will be called implicitly:\n1. If `--help` or `-h` is passed in as an argument.  (Note: in this case, the exit code will be set to 0)\n1. If `args.assertCount()` is called and the required number of arguments were not passed in.  For example, if args.assertCount(3) is called and only 2 arguments were passed in.\n1. If `env.assert()` is called and the environment variable(s) are not defined.\n\n## HTTP Request Helpers\n\nThe http helper can be used to make asynchronous HTTP requests. It returns a promise and resolves with an `IHttpResponse` object that contains these properties: `{ data, headers, statusCode, statusMessage, requestOptions }`.\n\nExample:\n\n```js\nconst response = await http(\"GET\", \"https://www.myapi.com);\n\necho(response.data) // { message: \"Testing\" }\necho(response.headers) // { \"Content-Type\": \"application/json\" }\necho(response.statusCode) // 200\necho(response.statusMessage) // \"OK\"\n```\n\nThere are also helpers for the primary HTTP methods: `http.get`, `http.post`, `http.put`, `http.patch`, and `http.delete`. These helpers do not require having to pass in the method type and will also return the response _body_ data. If the response is of JSON format, it will be parsed before being returned.\n\nExample:\n\n```js\nconst response = await http.get(\"https://www.myapi.com\");\n\necho(response) // { message: \"Testing\" }\n```\n\n### data\n\nYou can pass a `data` parameter which will then be sent as the body of the request.  If you pass a JavaScript object, it will be converted to JSON and `Content-Type`, `Accept`, and `Content-Length` will be be automatically set, unless specified differently.\n\n\n```js\nconst newTask = { name: \"My new task\", completed: false };\nawait http.post(\"https://www.myapi.com/tasks\", newTask, {\n  \"Authorization\": `Bearer ${env.API_AUTH_TOKEN}`\n});\n```\n\n#### Stream\nYou may also pass a readable Stream as `data`.  This is common when sending a file as part of a request.\n\n```js\nconst fs = require(\"fs\");\nconst filePath = \"./my_image.jpg\";\nconst data = fs.createReadStream(filePath);\nconst fileSize = fs.statSync(filePath).size;\nawait http.post(\"https://fakeimageserver.com/uploads\", data, {\n  \"Content-Type\": \"image/jpeg\",\n  \"Content-Length\": fileSize.toString(),\n});\n```\n\n_Note: If you are uploading a file, you can also use the `http.upload()` helper._\n\n### Default Headers\n\nIf any of the following headers are not specified, these default values will be used:\n\n| Header              | Value                                                  |\n| ------------------- | ------------------------------------------------------ |\n| `Accept`            | `*/*` or `application/json` if `data` is an object     |\n| `Accept-Encoding`   | `gzip`                                                 |\n| `Connection`        | `close`                                                |\n| `User-Agent`        | `jsh`                                                  |\n\n### Error Handling\n\nIf a status code outside the range 20X is returned in the HTTP response, a `HttpRequestError` error will be thrown. The error contains these properties: `{ message, data, statusCode, statusMessage, request, response }`.\n\nExample:\n\n```js\ntry {\n  const response = await http.post(\"https://www.myapi.com\", { data: \"1\" });\n} catch (err) {\n  echo(err.message) // \"400 Bad Request\"\n  echo(err.data) // { error: \"The 'data' property is formatted incorrectly\" }\n  echo(err.statusCode) // 400\n  echo(err.statusMessage) // Bad Request\n}\n```\n\n#### `noThrow` option\n\nYou can pass in the option `noThrow: true` when calling `http()` to prevent an error from being thrown when the response status is not 2xx. Instead, the response will be returned.\n\nExample:\n\n```js\nconst response = await http(\"POST\", \"https://www.myapi.com\", { data: 2 }, { noThrow: true });\n\necho(response.data) // \"A server error occurred.  Please try again later.\"\necho(response.headers) // { \"Content-Type\": \"text/plain\" }\necho(response.statusCode) // 500\necho(response.statusMessage) // \"Internal Server Error\"\n```\n\n### HTTP Request Options\n\n`http()` accepts an `options` parameter object that may contain any of the following fields:\n \n- `headers: object` - The request headers to send with the request.  A set of [default headers](#default-headers) will be included with the request even if not specified.\n- `timeout: number` - The number of milliseconds of inactivity before a socket is presumed to have timed out (Default: `120000` (2 minutes))\n- `noFollowRedirects: boolean` - Whether to not automatically follow 301 and 302 redirects. (Default: false)\n- `noThrow: boolean` - If set to true, will not throw if the response status code is not 2xx (Default: false)\n- `omitResponseBodyInErrorMessage: boolean` - If set to true, will not include the response body in a thrown error message (Default: false)\n\n## Installation\n\nNote: **jsh requires Node \u003e=16**\n\n### npx\n\nBy far the easiest way to use jsh is with a [npx](https://docs.npmjs.com/cli/v7/commands/npx) [shebang](\u003chttps://en.wikipedia.org/wiki/Shebang_(Unix)\u003e).\n\nCreate a file called `script.js`:\n\n```\n#!/usr/bin/env -S npx jsh\n\necho(\"Hello jsh\")\n```\n\n_Note: The above shebang includes multiple arguments which will not work in some enviroments (including older versions of Linux).  See [this post](https://github.com/TypeStrong/ts-node/issues/639#issuecomment-885817246) for a workaround._\n\nnpx will look for a globally installed (`npm install -g jsh`) or locally installed (package.json / `node_modules` ) version of jsh, and use it if found.  Otherwise, it will download the latest version from npm. Therefore, it is recommended to install jsh globally or locally when using npx so that it will be available and not have to be downloaded each time.\n\n### npm global install\n\nIf you don't want to use npx, you can install jsh globally with npm:\n\n```shell\nnpm install -g jsh\n```\n\nOnce it is installed globally, you can write your script with a jsh [shebang](\u003chttps://en.wikipedia.org/wiki/Shebang_(Unix)\u003e) which will allow your script to be executed directly, with the globally installed jsh loaded at runtime.  Run `which jsh` to locate the absolute path the jsh and use that for the shebang path.\n\n```js\n#!/usr/bin/env jsh\n\necho(`Hello jsh`)\n```\n\n### require\n\nRather than installing jsh globally, you can simply download it to a local folder and reference it directly from your script using a `require` or `import` statement. This is a good option for scripts running on a remote system where you may not have the ability to use npx or be able to install npm packages globally. Node.js will still need to be available, though.\n\nFirst, download jsh:\n\n```shell\ncurl -Lso jsh.cjs https://github.com/bradymholt/jsh/releases/latest/download/index.cjs\n```\n\nThen, in your script:\n\n```js\n#!/usr/bin/env node\nrequire('./jsh.cjs')\n\necho(`Hello jsh`)\n```\n\n## It's Still JavaScript\n\nWhen you write your shell scripts in jsh, you get to use a simple Bash like syntax but remember, it's still JavaScript! This means you can install npm packages and use them to your ❤️'s content.\n\nExample:\n\n```shell\nnpm install uuid\n```\n\n```ts\n#!/usr/local/bin/jsh\nrequire('uuid/v4')\n\necho(uuidv4()) // -\u003e '110ec58a-a0f2-4ac4-8393-c866d813b8d1'\n```\n\n## ES Modules\n\njsh is distributed as both a CommonJS and an ES Module library.  When you `require` or `import` jsh, Node should be able to determine which library to load based upon your file extension (.js, .cjs, .mjs) and/or \"type\" setting in your package.json file.\n\n## TypeScript Support\n\nTypeScript declarations for jsh are available and specified with `\"types\": \"index.d.ts\"` in the package.json file. A clean way to use TypeScript with jsh is by using [ts-node](https://github.com/TypeStrong/ts-node).\n\n1. Install ts-node, TypeScript, and jsh:\n    ```shell\n    npm init -y \u0026\u0026 npm install ts-node typescript jsh\n    ```\n1. Create your jsh script file using a `.ts` file extension, following the below example.\n    \n    _myscript.ts_\n    \n    ```ts\n    #!/usr/bin/env -S npx ts-node\n    import \"jsh\"\n    \n    const contents: string = \"Hello jsh from TypeScript\";\n    echo(contents)\n    ```\n    \n    _Note: The above shebang includes multiple arguments which will not work in some enviroments (including older versions of Linux).  See [this post](https://github.com/TypeStrong/ts-node/issues/639#issuecomment-885817246) for a workaround._\n    \n1. Run it: `chmod +x ./myscript.ts \u0026\u0026 ./myscript.ts`.\n\n### ES Modules\n\nYou can use jsh with TypeScript and ES Modules support so you can use features like [top-level await](https://v8.dev/features/top-level-await).\n\n1. Install ts-node, TypeScript, and jsh:\n    ```shell\n    npm init -y \u0026\u0026 npm install ts-node typescript jsh\n    ```\n1. Ensure you have a `package.json` file defined with (at least) `\"type\": \"module\"` specified:\n\n    ```json\n    {\n      \"type\": \"module\"\n    }\n    ```\n\n1. Ensure you have a `tsconfig.json` file defined with (at least) the following config:\n    ```json\n    {\n      \"compilerOptions\": {\n        \"target\": \"ESNext\",\n        \"module\": \"ESNext\",\n        \"moduleResolution\": \"node\"\n      }\n    }\n    ```\n 1. Create your jsh script, specifying `ts-node-esm` in the shebang:\n    \n    _myscript.ts_\n    \n    ```ts\n    #!/usr/bin/env -S npx ts-node-esm\n    import  \"jsh\"\n    \n    echo(\"Hello jsh from TypeScript\")\n    await new Promise((resolve) =\u003e setTimeout(resolve, 2000));\n    echo(\"Goodbye!\")\n    ```\n    \n    _Note: The above shebang includes multiple arguments which will not work in some enviroments (including older versions of Linux).  See [this post](https://github.com/TypeStrong/ts-node/issues/639#issuecomment-885817246) for a workaround._\n  1.  Run it: `chmod +x ./myscript.ts \u0026\u0026 ./myscript.ts`\n\n## GitHub Actions\n\nThe following inline syntax can be used for jsh inside of a [GitHub Actions](https://github.com/features/actions) [workflow file](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions).\n\n### JavaScript\n\n```yaml\njobs:\n  test:\n    runs-on: ubuntu-latest    \n    steps:      \n      - run: |\n          npm install jsh\n          node -r jsh \u003c\u003cEOF      \n            echo(\"Hello\");\n            setTimeout(()=\u003e echo.yellow(\"Sleepyhead\"), 5000);\n          EOF\n```\n\n### TypeScript (with ES Modules)\n```yaml\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - run: |\n          npm install ts-node jsh\n          node --loader ts-node/esm -r jsh --no-warnings --input-type=module \u003c\u003cEOF\n            echo(\"Hello\");\n            await new Promise((resolve) =\u003e setTimeout(resolve, 5000));\n            echo.yellow(\"Sleepyhead\");\n          EOF\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbradymholt%2Fjsh","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbradymholt%2Fjsh","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbradymholt%2Fjsh/lists"}