jsalt
v1.0.0
Published
Build reactive SPAs in a single .jsa file. No npm install. No bundler. Minimal token footprint — optimised for AI code generation and non-humans
Maintainers
Readme
JSA Framework
JSA (JS Alternative) — A minimal reactive framework where HTML + CSS + JS live in one .jsa file.
Quick Start
Install
npm install jsa-frameworkUsage
import { JSA, mount, load } from 'jsa-framework';
mount('#app', `
let count = 0
div = "Count: ${count}"
button @click = "setState('count', getState('count') + 1)" = "+"
`);
// Or load a .jsa file
load('counter.jsa', '#app');CDN (jsdelivr)
<script type="module">
// Load the runtime directly from jsdelivr
import { load } from 'https://cdn.jsdelivr.net/npm/jsalt/jsa-runtime.js';
load('./app.jsa', '#app');
</script>Examples
npm run demo
# Open http://localhost:3000Included:
examples/counter.jsa— Basic counter with computed valuesexamples/calculator.jsa— Full calculator (grid layout)examples/kanban.jsa— Kanban board (each loops, localStorage, lifecycle)examples/store.jsa— Todo list (bind, each, conditionals)examples/form.jsa— Form demo (bind, :attrs, if/show, watch, scoped CSS)examples/dashboard.jsa— Tailwind CDN dashboard (:class, html, :style, transitions, event modifiers)examples/composable.jsa— Reusable function pattern
Syntax
// State & Computed
let count = 0
let items = []
const doubled = computed(() => count * 2)
// Functions
fn inc = "setState('count', getState('count') + 1)"
// Watchers & Lifecycle
watch count = "console.log('count changed')"
on mount = "console.log('ready')"
// Scoped CSS
style
.card { background: white; border-radius: 8px }
.card:hover { transform: translateY(-2px) }
// UI with conditionals, loops, binding, dynamic attrs
div#app { display: flex }
h1 = "Count: ${count}"
div.list if = "${items.length > 0}"
div.item each = "${items}" = "${item.text}"
button @click = "inc()" = "+"v6 Features
| Feature | Syntax | Vue 3 Equivalent |
|---------|--------|-------------------|
| Conditional | if = "${expr}" | v-if |
| Toggle | show = "${expr}" | v-show |
| Loop | each = "${array}" | v-for |
| Binding | bind = "key" | v-model |
| Attrs | :disabled = "${!ok}" | v-bind |
| :class | :class = "bg-blue-500 p-4" | :class (merging) |
| :style | :style = "width: ${pct}%" | :style (dynamic) |
| Raw HTML | html = "<b>bold</b>" | v-html |
| Modifiers | @click.prevent = "..." | .prevent, .stop |
| Key mods | @keydown.enter = "..." | .enter, .esc |
| Transitions | transition = "fade" | <Transition> |
| Watch | watch key = "code" | watch() |
| Mount | on mount = "code" | onMounted() |
| Destroy | on destroy = "code" | onUnmounted() |
| Scoped CSS | style block | <style scoped> |
Handler API
| Function | Description |
|----------|-------------|
| setState(key, val) | Update state + re-render |
| getState(key) | Get current value |
| refs.name | DOM element reference |
| update() | Force re-render |
| item / idx | Current item/index in each loop |
AST CLI
Validate .jsa syntax for AI agents:
jsa-ast counter.jsa # Validate
jsa-ast counter.jsa --json # JSON AST
jsa-ast counter.jsa --tree # Tree view
jsa-ast *.jsa # Batch validateExit codes: 0 = valid, 1 = errors
Project Structure
jsa-framework/
├── jsa-runtime.js # Core runtime (~300 lines, published)
├── bin/jsa-ast.js # AST CLI (published)
├── package.json # NPM config
├── README.md # This file (published)
├── examples/ # Demo apps (not published)
│ ├── index.html # Demo shell (Tailwind CDN)
│ ├── counter.jsa
│ ├── calculator.jsa
│ ├── kanban.jsa
│ ├── store.jsa
│ ├── form.jsa
│ ├── dashboard.jsa # Tailwind showcase (v6)
│ └── composable.jsa
└── .agents/ # AI agent skills (not published)License
MIT
