npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mehran-ul-islam/ez-lang

v0.5.0-development.1

Published

Ez - a JS/Python-flavored scripting language that runs on Node

Readme

Ez

A small scripting language that blends JS-style braces with Python-style def-ish function syntax, built to run on Node. Files use the .ez extension.

Running a script

node index.js path/to/script.ez

Or install it as a global CLI:

npm install -g .
ez path/to/script.ez

Language guide

Variables

Only let — no var.

let name = "Mehran"
let count = 3

Printing

print.console("logs like console.log")
print.alert("goes to stderr, like a browser alert")

Functions

Define with function "Name" = { ... }. A zero-arg function can be invoked just by writing its bare name as a statement. Functions with parameters are called normally with parens.

function "Hello" = {
    print.console("Hello! world")
}
Hello              // calls it, zero-arg style

function "square"(x) = {
    return x * x
}
print.console(square(6))   // 36

Functions can recurse and return values normally with return.

Control flow

if (x < 10) {
    print.console("small")
} else {
    print.console("big")
}

while (i < 3) {
    print.console(i)
    i = i + 1
}

for (let j = 0; j < 3; j = j + 1) {
    print.console(j)
}

Arrays

let nums = [1, 2, 3]
print.console(nums[0])
nums[0] = 99

Input (reads a line from the terminal)

let n = input.from("your-name")
print.console("Hi " + n)

input.from("id") is a terminal stand-in for the web idea of document.getElementById — it can't reach into a real DOM from a CLI script (there isn't one), so it prompts on stdin instead, labeled by the id you pass.

Objects

let person = { name: "Mehran", age: 20 }
print.console(person.name)
person.age = 21

Array methods

let nums = [3, 1, 2]
nums.push(4)          // -> 4 (new length)
nums.sort()
print.console(nums)   // [1, 2, 3, 4]
print.console(nums.includes(2))   // true
print.console(nums.indexOf(2))    // 1
print.console(nums.join("-"))     // "1-2-3-4"
print.console(nums.length)

Also available: .pop(), .slice(start, end), .reverse().

String methods

let s = "  Hello World  "
print.console(s.trim().toUpperCase())
print.console(s.trim().split(" "))
print.console(s.includes("World"))
print.console(s.length)

Also available: .toLowerCase(), .replace(a, b), .indexOf(sub), .slice(start, end), .charAt(i).

Objects, arrays, and strings share .length as a plain property (no parens) — everything else above is a method call.

try/catch

try {
    let x = somethingThatDoesntExist
} catch (err) {
    print.console("caught: " + err)
}

Importing other .ez files

import "mathlib.ez"

print.console(square(5))

Paths resolve relative to the importing file. Each file is only imported once even if multiple files import the same thing. (This is a CLI-only feature — in the browser, use multiple <script type="text/ez"> tags instead, which already share one global scope.)

axios-style HTTP requests

let res = axios.get("https://api.example.com/users")
print.console(res.status)
print.console(res.data)

let created = axios.post("https://api.example.com/users", { name: "Mehran" })
axios.put("https://api.example.com/users/1", { name: "Updated" })
axios.delete("https://api.example.com/users/1")

Every axios.* call and get() also accepts an optional callback as the last argument, which makes the request non-blocking:

function "onDone"(res) = {
    print.console("got it: " + res.data)
}
get("https://api.example.com/thing", onDone)
print.console("this line runs immediately, before the response arrives")

Without a callback, both get() and axios.* still block until the response comes back, same as before.

REPL

Run ez with no file argument to drop into an interactive prompt:

$ ez
Ez REPL -- type Ez code, .exit to quit
ez> let x = 5
ez> x + 10
15
ez> .exit

Running in a browser (replacing inline JS)

There's also a browser build at browser/ez.js that lets .ez code run directly inside an HTML page — no Node needed on the visitor's end.

<script src="ez.js"></script>

<script type="text/ez">
    function "Hello" = {
        print.console("Ez loaded and running in the browser!")
    }
    Hello
</script>

Drop <script type="text/ez"> tags anywhere you'd normally put a JS <script> tag — inline, or with src="somefile.ez". They run in order, top to bottom, same as regular scripts, and share one global scope across all of them on the page.

In the browser, the built-ins behave differently than the CLI version, in ways that make more sense for a webpage:

  • input.from("id") → now genuinely does document.getElementById("id").value, reading a real form field off the page
  • print.alert(x) → a real window.alert(x) popup
  • print.console(x)console.log(x), shows up in devtools like normal
  • get(url) → a real HTTP GET, still made to look blocking/synchronous (Ez has no async/await). Under the hood this uses a deprecated synchronous XMLHttpRequest, which freezes page interaction while the request is in flight — fine for demos and small tools, not something to build a production site's data-fetching around.

Wiring up HTML events

Ez doesn't have its own onclick-style syntax yet, so call an Ez function straight from HTML using the EzRuntime bridge that ez.js exposes:

<input id="nameBox" type="text">
<button onclick="EzRuntime.call('greet')">Greet me</button>

<script type="text/ez">
    function "greet" = {
        let name = input.from("nameBox")
        print.console("Hi " + name)
    }
</script>

See browser/example.html for a full working page with this pattern.

  • lexer.js — turns source text into tokens
  • parser.js — recursive-descent parser, tokens → AST
  • interpreter.js — tree-walking evaluator, AST → running program
  • http-get-helper.js — child-process helper so get() can look synchronous
  • index.js — CLI entry point
  • examples/ — sample .ez scripts

What's next (not built yet)

  • Real browser/DOM support (input.from actually reaching a webpage) would need a separate transpile-to-JS mode, since a Node CLI script has no DOM.
  • Objects/dictionaries currently only exist as a runtime type produced by parsed JSON — there's no { key: value } literal syntax yet.
  • No module/import system yet for splitting a project across multiple .ez files.