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)
| File | Size | Uploaded | |
|---|---|---|---|
| myjs-0.0.5.tar.gz | 63.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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
|