JS Interop
Transpiled JS and client execution
@javascript
Decorator that transpiles Python functions to JavaScript for client-side execution.
from pulse import javascriptSignature
@overload
def javascript(fn: Callable[[*Args], R]) -> JsFunction[*Args, R]: ...
@overload
def javascript(*, jsx: bool = False) -> Callable[[Callable], JsFunction | Jsx]: ...Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
fn | Callable | - | Function to transpile (when used without arguments) |
jsx | bool | False | If True, transpiles as React component with props destructuring |
Usage
from pulse import javascript
from pulse.js import document, window
@javascript
def focus_element(selector: str):
document.querySelector(selector).focus()
@javascript
def get_scroll():
return {"x": window.scrollX, "y": window.scrollY}
# JSX mode for React components
@javascript(jsx=True)
def MyComponent(*children, title: str = ""):
return div(h1(title), *children)JsFunction
Wrapper for transpiled JavaScript functions. Returned by @javascript.
from pulse import JsFunctionProperties
| Property | Type | Description |
|---|---|---|
fn | Callable | Original Python function |
id | str | Unique identifier for deduplication |
deps | dict[str, Expr] | Resolved dependencies |
js_name | str | Unique JS function name (e.g., "myFunc_1") |
Methods
transpile
def transpile(self) -> FunctionTranspile to a Function AST node. Result is cached.
imports
def imports(self) -> dict[str, Expr]Get all Import dependencies.
functions
def functions(self) -> dict[str, JsFunction]Get all JsFunction dependencies.
Import
JavaScript import declaration with auto-registration and deduplication.
from pulse import ImportConstructor
Import(
name: str,
src: str | None = None,
*,
kind: Literal["named", "default", "namespace", "side_effect"] = "named",
is_type: bool = False,
lazy: bool = False,
version: str | None = None,
before: tuple[str, ...] | list[str] = (),
)Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
name | str | required | Import name (empty for side-effect imports) |
src | str | None | None | Module path or package name. If omitted, name is treated as the source and kind becomes "side_effect". |
kind | ImportKind | "named" | Import type: "named", "default", "namespace", "side_effect" |
is_type | bool | False | Type-only import (TypeScript) |
lazy | bool | False | Generate lazy import factory for code-splitting |
version | str | None | None | Package version constraint. Adds a dependency requirement used by the CLI |
before | tuple[str, ...] | () | Imports that must come before this one |
Usage
# Named import: import { useState } from "react"
useState = Import("useState", "react")
# Default import: import React from "react"
React = Import("React", "react", kind="default")
# Namespace import: import * as utils from "./utils"
utils = Import("utils", "./utils", kind="namespace")
# Side-effect import: import "./styles.css"
Import("", "./styles.css", kind="side_effect")
# Side-effect import (single-argument form)
Import("./styles.css")
# Lazy import for code-splitting
Chart = Import("Chart", "./Chart", kind="default", lazy=True)Properties
| Property | Type | Description |
|---|---|---|
js_name | str | Unique JS identifier |
is_local | bool | True if local file import |
is_lazy | bool | True if lazy import |
is_default | bool | True if default import |
is_namespace | bool | True if namespace import |
require
Register default npm package version constraints used by dependency resolution.
from pulse import requireSignature
def require(packages: Mapping[str, str]) -> NoneParameters
| Parameter | Type | Default | Description |
|---|---|---|---|
packages | Mapping[str, str] | required | Package name to version constraint |
Usage
import pulse as ps
ps.require(
{
"@mantine/core": ">=8.0.0",
"@mantine/dates": ">=8.0.0",
"dayjs": "^1",
}
)Call this at module import time to register requirements once. Import(..., version=...) also registers a requirement automatically.
react_component
Decorator that wraps an Import as a JSX component with a typed Python signature.
from pulse import react_component, Import, default_signature, ElementSignature
@overload
def react_component(expr: Expr) -> Callable[[Callable[P, Any]], Callable[P, Element]]
@overload
def react_component(
name: str,
src: str,
*,
lazy: bool = False,
is_default: bool = False,
) -> Callable[[Callable[P, Any]], Callable[P, Element]]Usage
from pulse import react_component, Import, default_signature, Element
@react_component(Import("Button", "@mantine/core"))
def Button(
*children,
variant: str = "filled",
disabled: bool = False,
) -> Element:
...
@react_component("Calendar", "react-calendar", is_default=True)
def Calendar(*children, **props) -> Element:
...
# Use in components
Button("Click me", variant="outline")default_signature
Default function signature for components without custom props:
def default_signature(
*children: Node,
key: str | None = None,
**props: Any
) -> Element: ...run_js
Execute JavaScript on the client during a callback.
from pulse import run_jsSignature
@overload
def run_js(expr: Expr, *, result: Literal[True]) -> asyncio.Future[Any]: ...
@overload
def run_js(expr: Expr, *, result: Literal[False] = ...) -> None: ...Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
expr | Expr | required | Expression from calling a @javascript function |
result | bool | False | If True, returns Future with JS return value |
Usage
from pulse import javascript, run_js
from pulse.js import document
@javascript
def focus_input(selector: str):
document.querySelector(selector).focus()
@javascript
def get_value(selector: str):
return document.querySelector(selector).value
# Fire and forget
def on_submit():
run_js(focus_input("#next-field"))
# Await result
async def on_check():
value = await run_js(get_value("#my-input"), result=True)
print(value)JsExecError
Exception raised when client-side JS execution fails.
from pulse import JsExecErrorclass JsExecError(Exception):
passpulse.js Module
JavaScript builtin bindings for use in @javascript functions.
from pulse.js import (
# Namespace objects (function-only modules)
Math, JSON, console, window, document, navigator, crypto, Intl,
# Classes (constructors + static methods)
AbortController, AbortSignal,
Array, ArrayBuffer, BigInt64Array, BigUint64Array, Blob, CustomEvent, DataView, Date,
DOMParser, Error, File, FileReader, FormData, Headers, Int16Array, Int32Array, Int8Array,
IntersectionObserver, Map, MutationObserver, Number, Object, PerformanceObserver, Promise,
RegExp, Request, ResizeObserver, Response, Set, String, TextDecoder, TextEncoder,
Uint16Array, Uint32Array, Uint8Array, Uint8ClampedArray, URL, URLSearchParams,
WeakMap, WeakSet, XMLSerializer,
# Functions
fetch,
# Statement functions
throw,
# Object literal helper
obj,
# Primitive values
undefined,
)Namespace Objects
Access methods directly:
Math.floor(3.7) # -> Math.floor(3.7)
JSON.stringify(data) # -> JSON.stringify(data)
console.log("hi") # -> console.log("hi")
window.scrollTo(0, 0) # -> window.scrollTo(0, 0)
document.querySelector("#app") # -> document.querySelector("#app")Classes
Create instances with new:
Date() # -> new Date()
Set([1, 2, 3]) # -> new Set([1, 2, 3])
Map() # -> new Map()
Array(10) # -> new Array(10)
# Static methods
Date.now() # -> Date.now()
Array.isArray(x) # -> Array.isArray(x)
Number.isFinite(42) # -> Number.isFinite(42)obj()
Create plain JavaScript object literals (not Maps):
from pulse.js import obj
obj(a=1, b=2) # -> { a: 1, b: 2 }
obj(**base, c=3) # -> { ...base, c: 3 }
obj() # -> {}throw()
Emit a JavaScript throw statement:
from pulse.js import throw, Error
throw(Error("Something went wrong")) # -> throw new Error("Something went wrong")Node Types
Node
Base type for all VDOM nodes.
from pulse import Node
Node = Element | Primitive | NoneElement
JSX element node.
from pulse import ElementPulseNode
Extended node type including components:
from pulse import PulseNode
PulseNode = Node | Component