Skip to main content

myjs

A JavaScript interpreter for Python. Run .js or .ts files, render HTML pages headlessly, script the DOM, call native C through ffi, and reach the entire Python ecosystem from JavaScript — no Node, no browser, no build step.

pip install myjs
# or, for an isolated CLI:
pipx install myjs
myjs                          # REPL (history, TAB completion, top-level await)
myjs script.js                # run a file
myjs script.ts                # run TypeScript -- .ts/.mts/.tsx auto-detected
myjs script.js a b            # ...with ["a","b"] as globalThis.argv
myjs -e "6 * 7"               # evaluate a snippet
myjs --typescript             # a TypeScript REPL (or -e / stdin, see below)
myjs page.html                # render a page headlessly -> HTML on stdout
myjs page.html -e "document.title"   # ...then read the rendered DOM
myjs examples                 # list the bundled demos; `myjs examples hn --run`
myjs examples cockpit --run   # a live TUI: dashboard, killable workers, raw keypresses
import myjs

myjs.eval("1 + 2 * 3")               # -> 7
myjs.run("script.js")

s = myjs.Session()                   # an isolated global scope
s.eval("const x = 21;"); s.eval("x * 2")   # -> 42

# a .py file calling a .js file directly -- no bridge, no serialization
mod = myjs.import_js("math_utils.js")   # export function double(x) { return x*2; }
mod.double(21)                          # -> 42, a real Python call into real JS

myjs.eval("const n: number = 42; n;", typescript=True)   # -> 42
mod = myjs.import_js("math_utils.ts")   # .ts/.mts auto-detected, no build step

What a script can reach

DOM document, window, and ~190 constructors (URL, Headers, Event, DOMRect, TextEncoder, …). document.createElement(...).appendChild(...) builds a real tree.
Event loop Promise (all / allSettled / race / any / finally), async / await, setTimeout / setInterval, queueMicrotask. Microtasks before timers.
ES modules import / export from .js files; a bare specifier resolves to a Python module (import np from "numpy").
fetch asynchronous, thread-backed — await Promise.all([fetch(a), fetch(b)]) is genuinely concurrent. WebSocket is a real RFC 6455 client.
fs / path / sh / http read & write files (fs.readBytes(path, n) for a raw header), run shell commands, serve HTTP (http.serve(port, handler)).
struct pre-imported — struct.unpack_from(fmt, fs.readBytes(path, n)) reads a BMP/PNG/ELF header with no library.
os / process Node-flavoured — os.platform(), os.cpus(), process.argv, process.env, process.exit().
ffi call any C library through ctypes — no node-gyp, no compiler.
py callable — py("2 ** 10") evaluates Python; py.import("pandas"), py.exec(...). The whole package index.
console %s / %d / %c format specifiers, console.table, console.group, console.count / time / assert.
native dialogs prompt() / confirm() / chooseFile() / chooseFolder() — the real macOS UI, driven from JS.
clipboard readText() / writeText() — the real system clipboard, shared with every other app.
plus say / notify / open, hash, atob / btoa, localStorage
const libc = ffi.loadLibrary("c");
libc.abs.argtypes = [ffi.types.int];
libc.abs.restype  = ffi.types.int;
libc.abs(-42);                              // 42

const rows = py.import("sqlite3").connect(":memory:").execute("SELECT 1+1").fetchall();
console.table([...py.list(py.list(rows)[0])]);

Headless HTML — render, scrape, automate

page = myjs.Page.load("https://example.com/")   # a URL (fetches HTML + CSS + JS) or a local file
page.eval("getComputedStyle(document.body).color")   # the fetched CSS, applied
page.fill("#search", "widgets")
page.submit("#form")
page.wait_for(".result")                # pump the event loop until it appears
page.text(".result")                    # read it back

myjs.render("app.html", strip_scripts=True)   # SSR: one call -> rendered HTML

Page.load takes a local path or an http(s):// URL. For a URL it fetches the page, every <link rel=stylesheet> (folded into the document so getComputedStyle sees the rules), and every <script src> — all resolved relative to the page URL. <script type="module"> gets module semantics; DOMContentLoaded / load fire. It's Puppeteer without a browser — a pip install, not a 150 MB Chromium download.

Command line

myjs [FILE] [ARGS...]           run a .js/.ts file (ARGS -> globalThis.argv),
                                or render a .html file headlessly
  -e, --eval JS                 evaluate JS; with a .html file, against the page
  -o, --output FILE             write rendered HTML to a file
  --html                        force headless-render mode
  --typescript                  erase TypeScript syntax in snippets, scripts and
                                the REPL (.ts/.mts/.tsx files auto-detected)
  --strip-scripts               drop <script> elements after they run
  --gui                         show the resulting DOM in a native window (needs myjs[gui])
  -i, --interactive             enter the REPL after running
myjs examples [NAME] [--run]    list / view / run the bundled demos
myjs                            REPL

The REPL

readline history (persisted to ~/.config/myjs/history), reverse search, and TAB completion of globals and obj.member chains. Multi-line input is auto-detected; top-level await works. Values print the way a real REPL's inspector would -- colored, wrapped past 72 columns, Map/Set/RegExp/ Date/Symbol/Promise shown natively rather than falling back to a generic dump. A live setInterval dashboard redraws in place instead of flashing the whole terminal. Commands: .load <file> (a .js, or a .html rendered into the session as page), .save, .editor, .clear, .help, .exit.

Language embedding

s = myjs.Session(scope={"answer": 42})
s.eval("answer * 2")                     # -> 84

A JS exception that escapes becomes myjs.JSError with js_name, js_line, and js_trace (the call stack). Sessions are isolated; console.log output is captured on s.console_lines.

The language layer passes a curated test262-style battery — closures, classes (extends / super / fields / getters-setters / private / static), destructuring, generators, labelled break, async / await, and the Array / String / Object / Number / Math / JSON / RegExp built-ins, real ES5-style prototype chains, and Symbol. Not covered: Proxy, with.

TypeScript

.ts/.mts files, myjs.eval(..., typescript=True), myjs.Session(typescript=True), myjs.import_js, and --typescript all lower TypeScript syntax and erase types on the AST before running it — no separate build step, no reparsing generated JavaScript. Type annotations, interfaces, enums, and decorators (legacy, experimentalDecorators-style) are supported; this erases types, it does not type-check. Declaration files (.d.ts) can't execute. See docs/myjs.md for the full picture (module resolution, .cts, error line numbers).

How it's built

myjs is the runtime layer on top of the domonic-libs acorn port — a faithful Python port of the acorn parser, a tree-walking evaluator, and a code generator (parse / interpret / generate, so JS can go source → AST → source) — running on domonic's DOM. It ships in lockstep with domonic-libs.

Full reference: docs/myjs.md. MIT licensed.

Metadata

Release files for myjs 0.0.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for myjs 0.0.5
File Size Uploaded
myjs-0.0.5.tar.gz 63.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for myjs 0.0.5
File Interpreter ABI Platform
myjs-0.0.5-py3-none-any.whl Python 3 none any Details

Total release size: 134.8 kB

Release files / myjs-0.0.5.tar.gz

Download URL myjs-0.0.5.tar.gz
Size 63.3 kB
Tags Source
SHA-256 checksum
How to use checksums
71efbeefeebd12a16ddf724d72ad6a6d71d3da142702912bcc5ac0aec34e8edb
BLAKE2b-256 checksum
How to use checksums
3e09ee44ed2dcb64e7670d8cbeacf88bb64987bfde5c68c62b3c50645ba4fe60
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.12

Release files / myjs-0.0.5-py3-none-any.whl

Download URL myjs-0.0.5-py3-none-any.whl
Size 71.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ffd661a707d62abfb9a8ce0cfa8cd65ae077bbf043535403c74e28ca12aa4511
BLAKE2b-256 checksum
How to use checksums
710dd430a408c6a576505c4947443490f33e7df02eb6be9e5162ca7e40dbb1b4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.12

Release history Release notifications | RSS feed

This release

0.0.5 This release

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page