# `js` – QuickJS-powered JavaScript interpreter for shell scripting
A fast, lightweight (~3 MB RAM), battery-included JavaScript runner built on QuickJS exclusively for writing clean, interactive, declarative and readable scripts.
## Key Features
- **Rich Interactive Prompts**
`ask()`, `confirm()`, `choose()`, `select()`, `search()`, `pick()` (file/dir picker), `describe()` (multiline), color picker
- **Dynamic Terminal UI**
Animated loaders, progress bars, paginated views: `render.loader()`, `render.levels()`, `render.pages()`
- **Text Graphics**
Tables (`draw.table()`), boxed text (`.border()`), stacking/joining strings
- **Deep System Integration**
`ls`, `cd`, `cwd`, `stat`, `ensureDir`, `exec()` / `execAsync()`, launch `$EDITOR`
- **Multi-format Parsing**
Built-in JSON, TOML, INI, CSV parsers/stringifiers
- **Enhanced Prototypes**
Dozens of utility methods on `String`, `Array`, `Object`, `Number` (colors, wrapping, trimming, etc.)
- **Powerful Piping**
Chain operations with `.pipe()` on any value
- **Easy I/O**
`read()`, `.write()`, automatic stdin handling
- **LSP Support**
Drop [types.d.ts](https://github.com/5hubham5ingh/js-util/blob/main/types.d.ts) next to your script → full TypeScript autocompletion, types, and diagnostics
- **Shell Globals**
`$HOME`, `$PATH`, `cwd`, etc. available directly
## Why use `js` instead of zx/awk/sed/jq?
- No process spawning for logic → dramatically faster for complex tasks
- One modern language instead of juggling multiple tools
- Interactive TUIs and styled output out of the box
- Tiny memory footprint (~3-4 MB vs 40-60 MB for Node/Deno/Bun)
- Full type safety and IDE support
Write concise, readable, and robust CLI tools in real JavaScript — without the overhead.
## Installation
`js` is distributed as a single binary.
1. **Download the latest release:** Grab the appropriate `js` binary for your Linux system from the [releases page](https://github.com/5hubham5ingh/js-util/releases) or run:
```bash
curl -L $(curl -s https://api.github.com/repos/5hubham5ingh/js-util/releases/latest | grep -Po '"browser_download_url": "\K[^"]+' | grep js) -o js
```
2. **Make it executable:**
```bash
chmod +x js
```
3. **Move it to your PATH:**
```bash
sudo mv js /usr/local/bin/
```
Now, `js` should be available globally in your terminal.
**Build from source:**
Run `curl -fsSL https://raw.githubusercontent.com/5hubham5ingh/js-util/main/build.sh | sh`
## Update
Run `js "__update"` to check and download update from Github releases.
## Usage
Run `js "__version"` to check version.
Run [examples.sh](https://github.com/5hubham5ingh/js-util/blob/main/examples.sh) bash script to see a demo:
```bash
bash examples.sh js
```
### Use in terminal
```bash
# Access environment variables
js "PATH.log()"
# /usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin
# List files in the current directory
js "ls.log()"
# [ "file1.txt", "file2.js", "directory1" ]
# Chain operations with .pipe()
js "ls.pipe(f => f.filter(f => f.endsWith('.js'))).join('\n').pipe(['fzf','--preview','bat {}'])"
# ...outputs the result
# Read a file and get its line count
js "read('my-file.txt').lines().length.log()"
# 42
# Execute external commands
js "exec('ls -la').log()"
# ...outputs the result of ls -la
# Read from stdin and process it
cat package.json | js "stdin.parseJson().version.log()"
# 1.0.0
# Create a styled border around text
js "'Hello, World!'.border('rounded', ['green']).log()"
# Use the full power of JavaScript's Math library, not just simple arithmetic
js "(Math.log(100) * Math.PI).log()"
# 14.476482730108398
# Prettify and inspect your PATH environment variable
js "PATH.split(':').join('\\n').border('rounded', ['yellow']).log()"
# ╭──────────────────╮
# │ /usr/local/bin │
# │ /usr/bin │
# │ /bin │
# │ /usr/sbin │
# │ /sbin │
# ╰──────────────────╯
# Count the number of markdown files in the current directory
js "ls.filter(f => f.endsWith('.md')).length.log()"
# 5
# List all subdirectories, styled with an icon
js "ls.filter(f => f.isDir.map(dir => \`📁 \${dir}\`.style('yellow')).join('\\n').log()"
# 📁 directory1
# 📁 another_dir
# Find and display all 'import' statements from a source file
js "read('index.js').lines().filter(l => l.trim().startsWith('import')).log()"
# [
# "import * as std from \"std\"",
# "import { exec as execAsync, execSync as exec } from \"../qjs-ext-lib/src/process.js\"",
# "import * as os from \"os\"",
# "import { ansi } from \"../justjs/ansiStyle.js\""
# ]
# Execute a shell command and process its output
js "'curl -s https://jsonplaceholder.typicode.com/todos/'.exec().parseJson().slice(0,4).table().log()"
╔════════╤════╤════════════════════════════════════╤═══════════╗
║ userId │ id │ title │ completed ║
╟────────┼────┼────────────────────────────────────┼───────────╢
║ 1 │ 1 │ delectus aut autem │ false ║
║ 1 │ 2 │ quis ut nam facilis et officia qui │ false ║
║ 1 │ 3 │ fugiat veniam minus │ false ║
║ 1 │ 4 │ et porro tempora │ true ║
╚════════╧════╧════════════════════════════════════╧═══════════╝
# List all 'dependencies' from a package.json file via stdin
cat package.json | js "stdin.parseJson().dependencies.keys().log()"
# [
# "some-dependency",
# "another-dependency"
# ]
# Display the current time in a fancy, styled box
js "new Date().toString().border('double', ['magenta', 'bold']).log()"
╔═══════════════════════════════════════════════════════════════════╗
║ Fri Jul 25 2025 11:08:00 GMT+0000 (Coordinated Universal Time) ║
╚═══════════════════════════════════════════════════════════════════╝
# Extend functionality by loading external scripts from '~/.config/js/' using `use(scriptName)`
js "use('colours'); fp = HOME.concat('/.config/pywall/colors'); read(fp).words().map(color.darker).join('\n').write(fp)"
# Exports colours.js and use functions defined in it, color.darker, to modify pywall colors
# Execute pre-written scripts
cat "$(ls ~/scripts/js/ | fzf)" | js
# Select and execute a JavaScript script from your scripts directory.
# Print array of objects as table
js "const t = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 },
{ name: 'Charlie', age: null }
]; t.table().log()"
╔═════════╤══════╗
║ name │ age ║
╟─────────┼──────╢
║ Alice │ 25 ║
║ Bob │ 30 ║
║ Charlie │ null ║
╚═════════╧══════╝
# Run interactively
js
# Starts a JavaScript REPL (Read-Eval-Print Loop) when no input is provided via stdin or command-line arguments.
# Print object as table, specify columns to include, join strings horizontally and stack vertically with specific alignment.
❯ t1 = ({ a: 1, b: 2, c: 3 }).table()
❯ t2 = [{ id: 1 }, { id: 2, extra: 'yes' }].table(['id'])
❯ h = 'Testing join() and stack()'.border()
❯ h.stack(t1.join(t2), 'center').log()
┌────────────────────────────┐
│ Testing join() and stack() │
└────────────────────────────┘
╔═════╤═══════╗╔════╗
║ key │ value ║║ id ║
╟─────┼───────╢╟────╢
║ a │ 1 ║║ 1 ║
║ b │ 2 ║║ 2 ║
║ c │ 3 ║╚════╝
╚═════╧═══════╝
# Create a markdown digest of a git repo
js "'git ls-files'.exec().lines().filter(f => f.endsWith('js'))
.reduce((digest,f)=> digest += '\n# FILE: ' + f + '\n' + read(f),'').log()"
```
### Use as script interpreter
`fzfDict.js`
```javascript
#!/usr/bin/js
if (scriptArgs.includes('-i')
) {
const screen = [];
const word = scriptArgs[scriptArgs.indexOf('-i') + 1].split(' ').join('%20');
exec(`curl -s https://api.dictionaryapi.dev/api/v2/entries/en/${word}`)
.parseJson()
.pipe((res) => {
if (Array.isArray(res)) {
const { word, phonetic, meanings } = res[0];
screen.push(word.toUpperCase().style('bold'), phonetic?.style('italic'), '');
return meanings
}
const { title, message, resolution } = res;
screen.push(word.toUpperCase().style('bold'), title.style(['bold', 'red']), message, resolution)
return [];
})
.for(({ partOfSpeech, definitions }) => {
screen.push(partOfSpeech?.style(['bold', 'italic']), ...definitions?.map(({ definition }) => `- ${definition}`), '')
})
print(screen.join('\n'))
} else {
const wordsCache = HOME.concat("/.cache/words.txt")
let file = std.open(wordsCache, "r");
if (!file) {
exec(`curl -o ${wordsCache} https://raw.githubusercontent.com/meetDeveloper/freeDictionaryAPI/refs/heads/master/meta/wordList/english.txt`)
file = std.open(wordsCache, "r");
}
file.readAsString()
.pipe(`fzf --ansi --preview-window=wrap,70% --bind "enter:preview(${os.realpath(scriptArgs[1])[0]} -i {})" --bind 'ctrl-j:preview-down' --bind 'ctrl-k:preview-up'`)
file.close()
}
```
```bash
chmod +x fzfDict.js
./fzfDict.js
```
# API Reference
## Extensions and config file
- Extra helper scripts can be loaded globally from `~/config/js/` with `use(scriptName)`.
- A default script, if exist, will be loaded from `~/.js` always.
> [!CAUTION]
> The docs are auto-generated from [types.d.ts](https://github.com/5hubham5ingh/js-util/blob/main/types.d.ts). For better reference, use the [types.d.ts](https://github.com/5hubham5ingh/js-util/blob/main/types.d.ts).
## Global Objects and Functions
### `globalThis` Extensions
The `globalThis` object is extended with several utility functions and properties.
#### `exec(command)`
Executes a shell command synchronously.
- **`command`**: (`string` or `array`) The command string or an array of command arguments.
- **Returns**: (`string`) The standard output of the command. Throws an error if the command fails.
- **Example**:
```javascript
const output = exec('ls -l');
print(output);
```
#### `execAsync(command)`
Executes a shell command asynchronously.
- **`command`**: (`string` or `array`) The command string or an array of command arguments.
- **Returns**: (`Promise`) A promise that resolves with the standard output of the command, or rejects with an error if the command fails.
- **Example**:
```javascript
execAsync('sleep 1 && echo "Done"').then(print);
```
#### `read(filePath)`
Reads the content of a file.
- **`filePath`**: (`string`) The path to the file.
- **Returns**: (`string`) The content of the file as a string. Throws an error if the file cannot be opened.
- **Example**:
```javascript
const content = read('./myfile.txt');
print(content);
```
#### `use(scriptName)`
Loads and executes a JavaScript script from a predefined configuration directory (`HOME/.config/js/`).
- **`scriptName`**: (`string`) The name of the script file (without the `.js` extension).
- **Returns**: (`undefined`)
- **Example**:
```javascript
use('myUtilityScript'); // Loads and runs ~/.config/js/myUtilityScript.js
```
#### `cd(directoryPath = HOME)`
Changes the current working directory.
- **`directoryPath`**: (`string`, optional) The path to the directory to change to. Defaults to `HOME`.
- **Returns**: (`boolean`) `true` if the directory was changed successfully, `false` otherwise.
- **Example**:
```javascript
cd('/tmp');
print(cwd); // Prints '/tmp'
```
#### `eval(expression)`
Evaluates a JavaScript expression or script string.
- **`expression`**: (`string`) The JavaScript expression or script to evaluate.
- **Returns**: (`any`) The result of the evaluated expression.
- **Example**:
```javascript
const result = eval('1 + 2'); // result is 3
```
#### `enquire`
An object containing functions for interactive terminal input. See [Enquire Module](https://github.com/5hubham5ingh/js-util?tab=readme-ov-file#enquire-1) for details.
#### `render`
An object containing functions for rendering animated or formatted output in the terminal. See [Render Module](https://github.com/5hubham5ingh/js-util?tab=readme-ov-file#render-1) for details.
#### `draw`
An object containing functions for drawing text-based UI elements in the terminal. See [Draw Module](https://github.com/5hubham5ingh/js-util?tab=readme-ov-file#draw-1) for details.
#### `parser`
An object containing functions for parsing various data formats (CSV, INI, TOML). See [Parser Module](https://github.com/5hubham5ingh/js-util?tab=readme-ov-file#parser-1) for details.
#### `log`
An object containing functions for logging message of different kinds. See [Log Module](https://github.com/5hubham5ingh/js-util?tab=readme-ov-file#log-1) for details.
#### `stat(path)`
Retrieves file system statistics for a given path.
- **`path`**: (`string`) The path to the file or directory.
- **Returns**: (`object` or `undefined`) An object containing `isDir`, `isFile`, `isLink`, `size`, `createdAt`, `modifiedAt` properties, or `undefined` if the path does not exist or an error occurs.
- **Example**:
```javascript
const fileStats = stat('./myfile.txt');
if (fileStats) {
print(`Size: ${fileStats.size} bytes`);
}
```
#### `ensureDir(directoryPath)`
Ensures that a directory exists, creating it and any necessary parent directories if they do not. Throws an error if a component of the path is an existing file.
- **`directoryPath`**: (`string`) The path of the directory to ensure.
- **Returns**: (`undefined`)
- **Example**:
```javascript
ensureDir('/tmp/my/new/directory');
```
#### `cwd`
- **Type**: (`string`)
- **Description**: A read-only global property that returns the current working directory.
- **Example**:
```javascript
print(cwd);
```
#### `ls`
- **Type**: (`Array`)
- **Description**: A read-only global property that returns an array of custom `String` objects representing the contents of the current working directory. Each `String` object has additional properties: `isDir`, `isFile`, `isLink`, `size`, `createdAt`, `modifiedAt`.
- **Example**:
```javascript
for (const item of ls) {
if (item.isDir) {
print(`[DIR] ${item}`);
} else {
print(`[FILE] ${item} (${item.size} bytes)`);
}
}
```
#### `stdin`
- **Type**: (`string`)
- **Description**: A read-only global property that returns the entire content of the standard input as a string. The content is cached after the first access.
- **Example**:
```javascript
// Assuming 'echo "hello world" | justjs -e "print(stdin)"' is run
print(stdin); // Prints "hello world\n"
```
## Object Prototype Extensions
### `Object.prototype`
#### `stringify(replacer = null, space = 2)`
Converts the object to a JSON string.
- **`replacer`**: (See `globalThis.stringify`)
- **`space`**: (See `globalThis.stringify`)
- **Returns**: (`string`)
- **Example**:
```javascript
({ a: 1, b: 2 }).stringify(); // '{\n "a": 1,\n "b": 2\n}'
```
#### `pipe(callback)`
Passes the object as an argument to a callback function.
- **`callback`**: (`function`) A function that takes one argument.
- **Returns**: (`any`) The result of the callback function.
- **Example**:
```javascript
({ value: 10 }).pipe(obj => obj.value * 2); // 20
```
#### `log()`
Prints the object to standard output and then returns the object. Useful for chaining.
- **Returns**: (`object`) The original object.
- **Example**:
```javascript
({ a: 1 }).log(); // Prints { a: 1 }, returns { a: 1 }
```
#### `entries()`
Returns an array of a given object's own enumerable string-keyed property `[key, value]` pairs.
- **Returns**: (`Array>`)
- **Example**:
```javascript
({ a: 1, b: 2 }).entries(); // [['a', 1], ['b', 2]]
```
#### `keys()`
Returns an array of a given object's own enumerable string-keyed property names.
- **Returns**: (`Array`)
- **Example**:
```javascript
({ a: 1, b: 2 }).keys(); // ['a', 'b']
```
#### `values()`
Returns an array of a given object's own enumerable string-keyed property values.
- **Returns**: (`Array`)
- **Example**:
```javascript
({ a: 1, b: 2 }).values(); // [1, 2]
```
#### `assign(sourceObject)`
Copies all enumerable own properties from one or more source objects to a target object.
- **`sourceObject`**: (`object`) The object to copy properties from.
- **Returns**: (`object`) The modified target object.
- **Example**:
```javascript
const obj = { a: 1 };
obj.assign({ b: 2 }); // obj is now { a: 1, b: 2 }
```
#### `table(columns)`
Generates a formatted text table from the object's data. If the object is an array of objects, it treats each object as a row. If it's a plain object, it treats its entries as key-value rows.
- **`columns`**: (`Array`, optional) An array of column headers to display.
- **Returns**: (`string`) A string representing the formatted table.
- **Example**:
```javascript
const data = [{ name: 'Alice', age: 30 }, { name: 'Bob', age: 24 }];
print(data.table());
// ╔═══════╤═════╗
// ║ name │ age ║
// ╟───────┼─────╢
// ║ Alice │ 30 ║
// ║ Bob │ 24 ║
// ╚═══════╧═════╝
```
#### `toIni()`
Converts the object to an INI-formatted string.
- **Returns**: (`string`)
- **Example**:
```javascript
const config = {
section1: {
key1: 'value1',
key2: 123
},
rootKey: 'rootValue'
};
print(config.toIni());
// rootKey=rootValue
//
// [section1]
// key1=value1
// key2=123
```
#### `toToml()`
Converts the object to a TOML-formatted string.
- **Returns**: (`string`)
- **Example**:
```javascript
const config = {
title: "TOML Example",
owner: {
name: "Alice",
dob: "1979-05-27T07:32:00-08:00"
}
};
print(config.toToml());
// title = "TOML Example"
//
// [owner]
// name = "Alice"
// dob = "1979-05-27T07:32:00-08:00"
```
### `Array.prototype`
#### `stringify(replacer = null, space = 2)`
Converts the array to a JSON string.
- **`replacer`**: (See `globalThis.stringify`)
- **`space`**: (See `globalThis.stringify`)
- **Returns**: (`string`)
- **Example**:
```javascript
[1, 2, 3].stringify(); // '[\n 1,\n 2,\n 3\n]'
```
#### `for(callback)`
Iterates over each element in the array, calling a callback function for each element. Returns the original array.
- **`callback`**: (`function`) A function that takes the current element as an argument.
- **Returns**: (`Array`) The original array.
- **Example**:
```javascript
[1, 2, 3].for(e => print(e * 2)); // Prints 2, 4, 6
```
#### `remove(...items)`
Removes the first occurrence of specified items from the array.
- **`...items`**: (`any`) One or more items to remove.
- **Returns**: (`Array`) The modified array.
- **Example**:
```javascript
const arr = [1, 2, 3, 2];
arr.remove(2); // arr is now [1, 3, 2]
```
#### `removeAll(...items)`
Removes all occurrences of specified items from the array.
- **`...items`**: (`any`) One or more items to remove.
- **Returns**: (`Array`) The modified array.
- **Example**:
```javascript
const arr = [1, 2, 3, 2];
arr.removeAll(2); // arr is now [1, 3]
```
#### `toCsvText(delimiter = ',')`
Converts an array of arrays (or an array of objects) to a CSV formatted string.
- **`delimiter`**: (`string`, optional) The delimiter to use. Defaults to `,`.
- **Returns**: (`string`) The CSV string.
- **Example**:
```javascript
[['Header1', 'Header2'], ['ValueA', 'ValueB']].toCsvText();
// "Header1,Header2\nValueA,ValueB"
```
#### `toCsvArray()`
Converts an array of objects (JSON format) to an array of arrays (CSV array format), including headers.
- **Returns**: (`Array>`) The CSV array.
- **Example**:
```javascript
[{ A: 1, B: 2 }, { A: 3, B: 4 }].toCsvArray();
// [['A', 'B'], ['1', '2'], ['3', '4']]
```
#### `toCsvJson(delimiter = ',')`
Converts an array of arrays (CSV array format) to an array of objects (CSV JSON format).
- **`delimiter`**: (`string`, optional) The delimiter (not directly used in this method, but might be relevant in internal parsing steps).
- **Returns**: (`Array