Fix bin/publish: use correct .env path for rspade_system Fix bin/publish script: prevent grep exit code 1 from terminating script 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
JQHTML Parser
The JQHTML parser converts template files into JavaScript functions that use the component system.
Quick Start - Common Patterns
Basic Component with innerHTML (Most Common)
<!-- Define a component with default attributes -->
<Define:Card tag="article" class="card" style="padding: 10px">
<div class="card-body">
<%= content() %> <!-- Outputs whatever innerHTML was passed -->
</div>
</Define:Card>
<!-- Usage - just pass innerHTML like regular HTML -->
<Card class="featured">
<h3>Card Title</h3>
<p>This is the card content.</p>
<button>Click me</button>
</Card>
<!-- Result: <article class="card featured Card Jqhtml_Component" style="padding: 10px"> -->
This content() pattern is the primary way to compose components in JQHTML. It works like regular HTML - the innerHTML you pass gets rendered where content() is called.
Container Component Example
<!-- A container with default size that can be overridden -->
<Define:Container $size="medium" class="container">
<div class="container-inner <%= this.args.size %>">
<%= content() %> <!-- Simple innerHTML output -->
</div>
</Define:Container>
<!-- Usage - override the default size -->
<Container $size="large">
<p>Any content here</p>
<UserList />
<Footer />
</Container>
Note: Slots (<#slotname>) are an advanced feature only needed when you have multiple distinct content areas. For 95% of use cases, the simple content() pattern shown above is recommended.
Define Tag Attributes (Component Configuration)
Define tags support three types of attributes for configuring components:
<Define:Contacts_DataGrid
extends="DataGrid_Abstract"
$ajax_endpoint=Frontend_Contacts_Controller.datagrid_fetch
$per_page=25
class="card DataGrid">
<div>Content here</div>
</Define:Contacts_DataGrid>
1. extends="" - Template Inheritance
Explicitly declare parent template for inheritance (without requiring a JavaScript class).
2. $property=value - Default Args
Set default values for this.args that component invocations can override:
- Quoted:
$user_id="123"→ String literal"123" - Unquoted:
$handler=MyController.fetch→ Raw JavaScript expression - These become defaults; invocations can override them
3. Regular Attributes
Standard HTML attributes like class="", tag="" applied to root element.
Generated Output:
{
name: 'Contacts_DataGrid',
tag: 'div',
defaultAttributes: {"class": "card DataGrid"},
defineArgs: {"ajax_endpoint": Frontend_Contacts_Controller.datagrid_fetch, "per_page": "25"},
extends: 'DataGrid_Abstract',
render: function() { ... }
}
Slot-Based Template Inheritance
When a template contains ONLY slots (no HTML), it automatically inherits the parent class template:
<!-- Parent: DataGrid_Abstract.jqhtml -->
<Define:DataGrid_Abstract>
<table>
<thead><tr><%= content('header') %></tr></thead>
<tbody>
<% for (let record of this.data.records) { %>
<tr><%= content('row', record) %></tr>
<% } %>
</tbody>
</table>
</Define:DataGrid_Abstract>
<!-- Child: Users_DataGrid.jqhtml (slot-only) -->
<Define:Users_DataGrid>
<#header>
<th>ID</th><th>Name</th><th>Email</th>
</#header>
<#row>
<td><%= row.id %></td>
<td><%= row.name %></td>
<td><%= row.email %></td>
</#row>
</Define:Users_DataGrid>
The parser detects slot-only templates and generates {_slots: {...}} format. Runtime walks prototype chain to find parent templates. Data passing: content('row', record) passes data to slot parameter function(row) { ... }. Reserved words: Slot names cannot be JavaScript keywords (function, if, etc.) - parser rejects with fatal error.
Lexer (Task 1 - COMPLETE)
The lexer is the first stage of the parser. It converts raw JQHTML template text into a stream of tokens.
Features
- No regex - Uses simple character scanning for maintainability
- Position tracking - Every token includes line, column, and absolute positions for source maps
- Simple token types - Clear, unambiguous token categories
- Efficient scanning - Single pass through the input
Token Types
TEXT- Plain HTML/text contentEXPRESSION_START-<%=opening tagCODE_START-<%opening tagTAG_END-%>closing tagIF,ELSE,ELSEIF,ENDIF- Conditional keywordsFOR,ENDFOR- Loop keywordsDEFINE_START,DEFINE_END- Component definition tagsCOMPONENT_NAME- Component identifierJAVASCRIPT- JavaScript code within tags
Usage
import { Lexer } from '@jqhtml/parser';
const template = `
<Define:MyComponent>
<h1><%= this.data.title %></h1>
<% if (this.data.show): %>
<p>Content here</p>
<% endif; %>
</Define:MyComponent>
`;
const lexer = new Lexer(template);
const tokens = lexer.tokenize();
// tokens is an array of Token objects with:
// - type: TokenType
// - value: string
// - line: number
// - column: number
// - start: number (absolute position)
// - end: number
Testing
# Build the parser
npm run build
# Run the test suite
node test-lexer.js
# Run the demo
node demo-lexer.js
Implementation Notes
The lexer uses a simple state machine approach:
- Text scanning - Default mode, captures plain text
- Tag detection - Looks for
<%,<%=,%>sequences - Keyword matching - After
<%, checks for control flow keywords - JavaScript capture - Captures code between tags
- Position tracking - Updates line/column for every character
No regex patterns are used. All scanning is done with:
match_sequence()- Check for exact string matchmatch_keyword()- Check for keyword with word boundary- Character-by-character advancement
Parser/AST Builder (Task 2 - COMPLETE)
The parser takes the token stream from the lexer and builds an Abstract Syntax Tree (AST) representing the template structure.
Features
- Recursive descent parsing - Simple, predictable parsing algorithm
- Clear node types - Each AST node represents a specific construct
- Position preservation - All nodes include source positions
- Error reporting - Clear messages with line/column information
AST Node Types
Program- Root node containing all top-level definitionsComponentDefinition- Component template definitionText- Plain text/HTML contentExpression- JavaScript expressions (<%= ... %>)IfStatement- Conditional rendering with optional elseForStatement- Loop constructsCodeBlock- Generic JavaScript code blocks
Usage
import { Lexer, Parser } from '@jqhtml/parser';
const template = `
<Define:Card>
<div class="card">
<h3><%= title %></h3>
<% if (showContent): %>
<p><%= content %></p>
<% endif; %>
</div>
</Define:Card>
`;
// Tokenize
const lexer = new Lexer(template);
const tokens = lexer.tokenize();
// Parse to AST
const parser = new Parser(tokens);
const ast = parser.parse();
// AST structure:
// {
// type: 'Program',
// body: [{
// type: 'ComponentDefinition',
// name: 'Card',
// body: [...]
// }]
// }
Testing
# Run the parser test suite
node test-parser.js
# Run the interactive demo
node demo-parser.js
Implementation Notes
The parser uses straightforward techniques:
- Token consumption - Advances through tokens one at a time
- Lookahead - Peeks at upcoming tokens to decide parsing path
- Context tracking - Knows when inside components, loops, etc.
- Error recovery - Provides helpful error messages
No parser generators or complex algorithms - just simple recursive functions that build nodes.
Code Generator (Task 3 - COMPLETE)
The code generator takes the AST and produces executable JavaScript functions that work with the JQHTML component system.
Features
- jQuery-based DOM manipulation - Generates efficient jQuery code
- Component render functions - Each template becomes a render function
- Control flow handling - Properly handles if/else and for loops
- Expression evaluation - Safely evaluates and renders expressions
- Colon syntax support - Strips trailing colons from control statements
Generated Code Structure
// Each component gets a render function
jqhtml_components.set('ComponentName', {
name: 'ComponentName',
render: function render() {
const $root = $('<div></div>');
const $current = $root;
// Generated DOM manipulation code here
return $root.children();
},
dependencies: []
});
Usage
import { Lexer, Parser, CodeGenerator } from '@jqhtml/parser';
const template = `
<Define:MyComponent>
<h1><%= this.data.title %></h1>
<% if (this.data.items): %>
<ul>
<% for (const item of this.data.items): %>
<li><%= item %></li>
<% endfor; %>
</ul>
<% endif; %>
</Define:MyComponent>
`;
// Generate the code
const lexer = new Lexer(template);
const tokens = lexer.tokenize();
const parser = new Parser(tokens);
const ast = parser.parse();
const generator = new CodeGenerator();
const result = generator.generate(ast);
// Use in a component
class MyComponent extends Component {
async on_render() {
const template = result.components.get('MyComponent');
const elements = template.render.call(this);
this.$.append(elements);
}
}
Testing
# Run the code generator test suite
node test-codegen.js
# Run the interactive demo
node demo-codegen.js
# View the integration example
# Open example-integration.html in a browser
Implementation Notes
The code generator:
- Traverses the AST - Visits each node and generates appropriate code
- Maintains context - Tracks current jQuery element for appending
- Handles nesting - For loops create temporary containers
- Strips syntax - Removes trailing colons from control flow
- Escapes strings - Properly escapes text content for JavaScript
v1 JavaScript Compilation Analysis
A comprehensive analysis of how JQHTML v1 compiles templates to JavaScript has been documented in JQHTML_V1_JAVASCRIPT_COMPILATION_PATTERNS.md. This analysis reveals:
- Instruction-based output: Templates compile to arrays of rendering instructions
- Three instruction types:
{tag:...},{comp:...},{block:...} - Context preservation: The
_that = thispattern for nested functions - Single-pass DOM construction: Efficient rendering with one
$.html()call - Deferred component initialization: Two-phase process for component creation
These patterns inform v2 implementation decisions, particularly around maintaining the efficient instruction-based architecture while modernizing the JavaScript output.
Implementation Status (December 2024)
✅ Completed Tasks
- Lexer - Full tokenization with position tracking
- Parser - AST generation with all v2 syntax
- Code Generator - Instruction array generation
- Component Integration - Runtime integration complete
- Slot System -
<#name>syntax fully implemented - Nested Components -
<ComponentName>recognition - Binding Syntax -
:propand@eventsupport
Current Features
- Template Syntax: HTML with embedded JavaScript
- Control Flow:
if/else/elseifandforloops (both colon and brace styles) - Components:
<Define:Name>definitions and<ComponentName>invocations - Slots:
<#name>content</#name>and<#name />syntax - Expressions:
<%= expression %>for output - Bindings:
:text,:value,:class,:stylefor data binding - Events:
@click,@change, etc. for event handlers - Scoped IDs:
$idattribute for component-scoped IDs
Instruction Format
The parser generates v1-compatible instruction arrays:
// HTML tags
{tag: ["div", {"class": "card"}, false]} // open tag
{tag: ["div", {}, true]} // close tag
// Components
{comp: ["UserCard", {name: "John"}]}
// Slots
{slot: ["header", {}, (props) => {
const _output = [];
_output.push("Header content");
return [_output, this];
}]}
// Text content
"Plain text"
// Expressions
{expr: () => this.data.title}
Integration with Core
Templates compile to functions that return [instructions, context]:
function render() {
const _output = [];
// Generate instructions...
_output.push({tag: ["h1", {}, false]});
_output.push("Hello World");
_output.push({tag: ["h1", {}, true]});
return [_output, this];
}
The core runtime's instruction processor handles these arrays to create DOM.
Next Steps
- Build Tools - Webpack loader for
.jqhtmlimports - Source Maps - Full debugging support
- VS Code Extension - Syntax highlighting
- Autoformatter - Code formatting for
.jqhtmlfiles