@brandonsussman/jmapper
v0.1.0
Published
Use JSON markup in react instead of html
Readme
JMapper
JMapper is a framework that renders JML (JSON Markup Language) by harvesting local React lexical scope via a Babel-powered Vite plugin. UI is declared as data; component logic stays in React with a zero-JSX return: return JMapper(schema).
SDUI Architecture
- Schema-driven UI (SDUI): The component returns a single call
JMapper(schema). The schema is JSON that describes the tree of components/nodes. - Scope injection: A Vite plugin (Babel visitor) finds
JMapper(schema)and injects the function’s lexical scope as the second argument:JMapper(schema, { user, items, show, set, ... }). - Runtime:
JMapper(schema, scope)recursively renders the schema withReact.createElement, evaluating directives and interpolations againstscope.
JML Specification
| Feature | JML Example |
|--------|--------------|
| Interpolation | "text": "Hello {{user.name}}" |
| Conditionals | "if": "show", "type": "Modal" |
| Loops | "repeat": "items", "type": "li", "props": {"key": "item.id"}, "text": "{{item.text}}" |
| Events | "onClick": "set(1)" or "onClick": "toggle()" |
type: React component or HTML tag name.props: Props object; values can use{{...}}interpolation.text: Text content; supports{{...}}.children: Array of child JML nodes.if: Optional; expression string. If falsy, render nothing.repeat: Optional; array name in scope. Renders once per item;itemandindexin scope for children.on*: Event handlers as string expressions (e.g.onClick,onChange), evaluated in scope.
Using in other projects
Install as a dependency (from npm when published, or locally via npm link / file:):
npm install jmapper1. Runtime – use the renderer and types in your app:
import { JMapper, JMapperErrorBoundary } from 'jmapper';
import type { JMLSchema, JMapperScope, JMapperOptions } from 'jmapper';
const schema = { type: 'div', text: 'Hello {{name}}' };
const scope = { name: 'World' };
export function App() {
return JMapper(schema, scope);
}2. Vite plugin – for automatic scope injection (return JMapper(schema) with no second argument), add the plugin before the React plugin:
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import jmapperPlugin from 'jmapper/plugin';
export default defineConfig({
plugins: [jmapperPlugin(), react()],
});Your component can then return JMapper(schema); the plugin injects the function’s lexical scope as the second argument. The plugin is TypeScript; use a Vite config that supports .ts (e.g. vite.config.ts) or ensure the plugin is resolved by your build.
3. Local / link usage
cd /path/to/JMapper && npm run build:lib
cd /path/to/your-app && npm install /path/to/JMapperThen import { JMapper } from 'jmapper' and import jmapperPlugin from 'jmapper/plugin' as above.
Project layout
dist-lib/— Built runtime (generated bynpm run build:lib).plugins/vite-plugin-jmapper.ts— Vite plugin source.src/lib/— Runtime source (JMapper, types).src/examples/— Example Dashboard and schema.
Commands
npm run dev— Start Vite dev server (demo app).npm run build— Build demo app for production.npm run build:lib— Build the library intodist-lib(run before publishing or when using viafile:).npx vitest run— Run tests.
Zero-JSX Rule
Components using JMapper must return only JMapper(schema). No JSX in the return path; the Babel plugin supplies the scope automatically.
