Fix bin/publish: copy docs.dist from project root

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>
This commit is contained in:
root
2025-10-21 02:08:33 +00:00
commit f6fac6c4bc
79758 changed files with 10547827 additions and 0 deletions

21
node_modules/@jqhtml/parser/LICENSE generated vendored Executable file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 JQHTML Team
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

740
node_modules/@jqhtml/parser/LLM_REFERENCE.md generated vendored Executable file
View File

@@ -0,0 +1,740 @@
# JQHTML v2 Complete Syntax & Instruction Format Specification
## Overview
JQHTML v2 is a composable component templating language for jQuery that compiles to efficient JavaScript instruction arrays. This document defines the complete syntax specification and the instruction format that templates compile to.
## Critical Concepts
### The Instruction Array Pattern
Templates compile to functions that return arrays of rendering instructions:
```javascript
// Every template function returns this structure:
function render(Component, data, args, content) {
const _output = []; // Instruction array
// ... push instructions ...
return [_output, this]; // CRITICAL: Returns tuple [instructions, context]
}
```
### Why This Pattern Exists
1. **Deferred Execution**: Instructions can be processed when optimal
2. **Single DOM Write**: All HTML built as string, then one `$.html()` call
3. **Complex Attribute Handling**: Functions and objects can't go in HTML strings
4. **Component Coordination**: Parent/child relationships preserved through execution
## Template Syntax
### 1. Component Definition
Components are defined using the `<Define:>` tag:
```jqhtml
<Define:ComponentName>
<!-- Component template -->
</Define:ComponentName>
```
### 2. $ Attribute System
JQHTML uses the `$` prefix for passing data to components as `this.args`:
#### Syntax Rules
1. **Quoted = Literal String**
```jqhtml
$title="User Profile" → args.title = "User Profile"
$expr="this.user" → args.expr = "this.user" (the string, not evaluated!)
```
2. **Unquoted = JavaScript Expression**
```jqhtml
$user=this.user → args.user = {actual user object}
$count=42 → args.count = 42
$enabled=true → args.enabled = true
$handler=this.onClick → args.handler = function reference
```
3. **Parentheses for Complex Expressions**
```jqhtml
$status=(active ? 'online' : 'offline')
$data=({...this.user, modified: true})
$items=(this.data.items || [])
```
#### Runtime Behavior
- All $ attributes become properties in `this.args` (NOT `this.data`)
- Stringable values (strings/numbers) appear as `data-*` DOM attributes
- Complex values (objects/arrays/functions) accessible only via `this.args`
- Component constructor syncs stringable args to DOM
#### Example
```jqhtml
<!-- Parent component with this.user = {name: "Alice", id: 123} -->
<UserCard
$user=this.user <!-- args.user = {name: "Alice", id: 123} -->
$title="User Profile" <!-- args.title = "User Profile" -->
$count=42 <!-- args.count = 42 -->
data-role="admin" <!-- args.role = "admin" -->
/>
```
Result in UserCard component:
- `this.args.user` = {name: "Alice", id: 123} (object)
- `this.args.title` = "User Profile" (string)
- `this.args.count` = 42 (number)
- `this.args.role` = "admin" (from data- attribute)
DOM shows: `<div data-title="User Profile" data-count="42" data-role="admin">` (no data-user since it's an object)
### 3. Expressions
Output JavaScript expressions using `<%= %>` (escaped) or `<%!= %>` (unescaped):
```jqhtml
<%= this.data.title %> <!-- Escaped output -->
<%!= this.data.rawHtml %> <!-- Unescaped output -->
<%@= this.data.maybeUndefined %> <!-- Escaped with error suppression -->
<%!@= this.data.maybeUndefinedHtml %> <!-- Unescaped with error suppression -->
```
### 3. Code Blocks
Execute JavaScript code using `<% %>`. Regular JavaScript passes through unchanged:
```jqhtml
<% const items = this.data.items || []; %>
<% console.log('Rendering:', items.length); %>
<!-- Regular JavaScript control flow -->
<% if (condition) { %>
<p>True branch</p>
<% } else { %>
<p>False branch</p>
<% } %>
<% for (const item of items) { %>
<div><%= item.name %></div>
<% } %>
```
**Design Rationale**: JavaScript code blocks pass through directly into the generated function. This allows developers to use any JavaScript construct without parser limitations. The parser only intervenes for template-specific syntax that isn't valid JavaScript (like the colon-style below).
### 4. Template Control Flow
For template-specific control flow, colon style provides PHP-like syntax:
```jqhtml
<!-- Colon style (requires endif/endfor) -->
<% if (condition): %>
<p>True branch</p>
<% else: %>
<p>False branch</p>
<% endif; %>
<% for (const item of items): %>
<div><%= item.name %></div>
<% endfor; %>
```
Note: The brace style shown in Code Blocks above is regular JavaScript, not special template syntax.
## Component Invocation
### Basic Component Usage
Components are invoked using capital-letter tags:
```jqhtml
<UserCard /> <!-- Self-closing, no content -->
<UserCard></UserCard> <!-- Empty paired tags, no content -->
<UserCard>Default content</UserCard> <!-- With innerHTML content (most common) -->
```
### Property Passing
```jqhtml
<!-- String literals -->
<UserCard name="John Doe" role="admin" />
<!-- String interpolation -->
<UserCard title="Welcome <%= this.data.userName %>!" />
<div class="card <%= this.data.active ? 'active' : '' %>">
<!-- Data attributes ($property syntax) -->
<UserCard $user=this.data.currentUser $active=true />
<!-- Compiles to: data-user and data-active attributes -->
<!-- Property binding (:property syntax) -->
<UserCard :user="currentUser" :settings="{ theme: 'dark' }" />
<!-- Compiles to: data-bind-user and data-bind-settings attributes -->
<!-- Event binding (@event syntax) -->
<UserCard @save="handleSave" @cancel="() => closeModal()" />
<!-- Compiles to: data-on-save and data-on-cancel attributes -->
```
### $ Attribute System
JQHTML uses the `$` prefix as a shorthand for data attributes with special handling:
#### General Case: `$foo="bar"``data-foo`
```jqhtml
<div $user=currentUser $theme="dark" $count=42>
<!-- Compiles to: -->
{tag: ["div", {"data-user": currentUser, "data-theme": "dark", "data-count": 42}, false]}
```
**Runtime Behavior**:
1. The attribute is set via jQuery's `.data()` method: `$element.data('user', currentUser)`
2. For debugging visibility, if the value is a string or number, it's also set as a DOM attribute
3. Objects and arrays are stored in `.data()` but not visible in DOM
#### Special Case: `$id="name"` → Scoped IDs
The `$id` attribute has special handling for component-scoped element selection:
```jqhtml
<Define:UserCard>
<div $id="container">
<input $id="username" type="text" />
<button $id="submit">Submit</button>
</div>
</Define:UserCard>
```
**Compilation**:
```javascript
// In render function (receives _cid parameter)
function render(_cid) {
const _output = [];
_output.push({tag: ["div", {"id": "container:" + _cid}, false]});
_output.push({tag: ["input", {"id": "username:" + _cid, "type": "text"}, false]});
_output.push({tag: ["button", {"id": "submit:" + _cid}, false]});
// ...
}
```
**Runtime Usage**:
```javascript
class UserCard extends Component {
init() {
// Find scoped elements
const $username = this.$id('username'); // Returns $('#username:123')
const $submit = this.$id('submit'); // Returns $('#submit:123')
$submit.on('click', () => {
const value = $username.val();
// ...
});
}
}
```
#### Lexical Scoping of `_cid`
Component IDs flow through lexical scope naturally:
```jqhtml
<Define:ParentComponent>
<div $id="parent-element"> <!-- Gets ParentComponent's _cid -->
<ChildComponent>
<div $id="slot-element" /> <!-- Also gets ParentComponent's _cid -->
</ChildComponent>
</div>
</Define:ParentComponent>
<Define:ChildComponent>
<div $id="child-element"> <!-- Gets ChildComponent's _cid -->
<%= content() %> <!-- Slot content preserves parent's _cid -->
</div>
</Define:ChildComponent>
```
This happens because content functions capture their defining scope:
```javascript
// ParentComponent render
function render(_cid) { // Parent's _cid = 123
_output.push({comp: ["ChildComponent", {}, () => {
// This arrow function captures Parent's _cid
const _output = [];
_output.push({tag: ["div", {"id": "slot-element:" + _cid}, false]}); // slot-element:123
return [_output, this];
}]});
}
// ChildComponent render
function render(_cid) { // Child's _cid = 456
_output.push({tag: ["div", {"id": "child-element:" + _cid}, false]}); // child-element:456
}
```
#### Attribute Value Interpolation
Within quoted attribute values, `<%= %>` and `<%!= %>` perform string interpolation:
```jqhtml
<!-- Both of these work identically in attributes -->
<div title="User: <%= user.name %>">
<div title="User: <%!= user.name %>">
<!-- Compiles to -->
{tag: ["div", {"title": "User: " + user.name}, false]}
```
**Design Rationale**: Inside attribute values, HTML escaping is inappropriate because we're building a JavaScript string, not HTML content. The JavaScript string serialization handles all necessary escaping (quotes, backslashes, etc.). This is semantically different from top-level `<%= %>` which outputs to HTML and requires escaping.
## Binding Syntax (v2)
### Property Binding with `:`
The `:` prefix creates dynamic property bindings that are always evaluated as JavaScript expressions:
```jqhtml
<!-- Simple property binding -->
<input :value="formData.name" :disabled="isLoading" />
<!-- Complex expressions -->
<div :style="{ color: textColor, fontSize: size + 'px' }" />
<!-- Method calls -->
<select :options="getOptions(category)" />
```
**Compilation**:
- `:prop="expr"``{"data-bind-prop": expr}` (no quotes around expr)
- Values are ALWAYS treated as JavaScript expressions
- Even quoted strings like `:prop="value"` become expressions
### Event Binding with `@`
The `@` prefix binds event handlers:
```jqhtml
<!-- Simple handler -->
<button @click="handleClick">Click Me</button>
<!-- With arguments -->
<button @click="deleteItem(item.id)">Delete</button>
<!-- Inline arrow function -->
<input @input="(e) => updateValue(e.target.value)" />
<!-- Multiple events -->
<form @submit="save" @reset="clear">
```
**Compilation**:
- `@event="handler"``{"data-on-event": handler}`
- Handlers are JavaScript expressions (function references or inline functions)
### Binding Design Rationale
1. **Vue.js Familiarity**: Uses the same `:prop` and `@event` syntax as Vue.js
2. **Always Expressions**: Unlike `$` attributes which distinguish strings vs expressions, bindings are ALWAYS expressions
3. **Runtime Processing**: The runtime will need to handle these specially:
- `data-bind-*` attributes set properties dynamically
- `data-on-*` attributes attach event listeners
4. **Separate from `$` System**: Bindings serve a different purpose than data attributes
## Component Content & innerHTML (Primary Pattern)
### Using `content()` Function - The Most Common Approach
**The vast majority of components will use the simple `content()` function to output innerHTML passed by parent components.** This is the primary, recommended pattern for component composition:
```jqhtml
<!-- Define a component that outputs innerHTML -->
<Define:Container>
<div class="container">
<div class="container-body">
<%= content() %> <!-- Outputs whatever innerHTML was passed -->
</div>
</div>
</Define:Container>
<!-- Usage - just pass innerHTML like regular HTML -->
<Container>
<p>This is the content</p>
<span>More content here</span>
<!-- Any HTML/components can go here -->
</Container>
```
### Common Patterns with `content()`
```jqhtml
<!-- Card component with innerHTML -->
<Define:Card>
<div class="card">
<div class="card-body">
<%= content() %> <!-- Simple innerHTML output -->
</div>
</div>
</Define:Card>
<!-- Panel with conditional innerHTML -->
<Define:Panel>
<div class="panel">
<% if (this.args.title): %>
<div class="panel-header"><%= this.args.title %></div>
<% endif; %>
<div class="panel-body">
<%= content() %>
</div>
</div>
</Define:Panel>
<!-- Wrapper that adds behavior to innerHTML -->
<Define:Collapsible>
<div class="collapsible" $id="wrapper">
<button $onclick="toggle">Toggle</button>
<div class="content" $id="content">
<%= content() %> <!-- Wrapped innerHTML -->
</div>
</div>
</Define:Collapsible>
```
### How `content()` Works
1. **Parent passes innerHTML**: When invoking `<Component>innerHTML here</Component>`
2. **Template receives it**: The `content` parameter in render function contains the innerHTML
3. **Output with `<%= content() %>`**: Call the function to output the HTML where needed
4. **No special syntax needed**: Just regular HTML/components as children
### Important Rules
- **No mixing**: If a component uses ANY slots (`<#slotname>`), ALL content must be in slots
- **Most components don't need slots**: The simple `content()` pattern handles 95% of use cases
- **Slots are for advanced scenarios**: Only use slots when you need multiple named content areas
## Slot System (Advanced Feature)
**Note: Slots are an advanced feature for specific use cases. Most components should use the simpler `content()` pattern shown above.**
### When to Use Slots vs `content()`
Use `content()` (recommended for most cases):
- Single content area
- Wrapping/decorating content
- Simple component composition
- Standard container components
Use slots (advanced cases only):
- Multiple distinct content areas (header/body/footer)
- Complex layouts with specific placement
- Data table templates with row/header/footer sections
### 1. Named Slots
Pass content to specific slots using `<#slotname>` syntax:
```jqhtml
<DataTable $items=this.data.users>
<#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>
<#empty /> <!-- Self-closing slot -->
</DataTable>
```
### 2. Accessing Slots in Components (Advanced)
For the rare cases where slots are needed, components can check for and render specific named slots:
```jqhtml
<Define:Card>
<div class="card">
<!-- Render default slot -->
<%= content() %>
<!-- Render named slot -->
<% if (content('footer')) { %>
<footer>
<%= content('footer', { timestamp: new Date() }) %>
</footer>
<% } %>
</div>
</Define:Card>
```
## Instruction Format Reference
### Instruction Types
Templates compile to three instruction types:
1. **`{tag: [...]}`** - HTML elements
2. **`{comp: [...]}`** - Component invocations
3. **`{slot: [...]}`** - Slot definitions
### 1. Tag Instruction
```javascript
{tag: [tagname, attributes, self_closing]}
```
**Format:**
- `tagname`: String - HTML tag name
- `attributes`: Object - Tag attributes (may contain functions!)
- `self_closing`: Boolean - Whether tag self-closes
**Examples:**
```javascript
{tag: ["div", {"class": "user-card"}, false]}
{tag: ["img", {"src": "/avatar.jpg", "alt": "User"}, true]}
{tag: ["button", {"onclick": this.handleClick}, false]}
{tag: ["div", {"data-id": "header"}, false]} // $id becomes data-id
```
### 2. Component Instruction
```javascript
{comp: [component_name, attributes, optional_innerhtml_function]}
```
**Format:**
- `component_name`: String - Component to instantiate
- `attributes`: Object - Props/attributes to pass
- `optional_innerhtml_function`: Function - Contains slots and default content
**CRITICAL**: When a component invocation contains slots or any child content, it MUST use the three-parameter form with a content function. Slots are NOT separate instructions after the component - they are contained within the component's content function.
**Examples:**
```javascript
// Simple component (no children)
{comp: ["UserAvatar", {"data-userId": this.data.id}]}
// Component with slots
{comp: ["DataTable", {"data-rows": this.data.items}, (DataTable) => {
const _output = [];
_output.push({slot: ["header", {}, (header) => {
const _output = [];
_output.push({tag: ["th", {}, false]});
_output.push("Name");
_output.push("</th>");
return [_output, this];
}]});
return [_output, this];
}]}
```
### 3. Slot Instruction
```javascript
{slot: [slot_name, attributes, render_function]}
```
**Format:**
- `slot_name`: String - Slot identifier
- `attributes`: Object - Props for slot
- `render_function`: Function - Returns `[instructions, context]`
**Example:**
```javascript
{slot: ["actions", {}, (actions) => {
const _output = [];
_output.push({tag: ["button", {"onclick": this.delete}, false]});
_output.push("Delete");
_output.push("</button>");
return [_output, this];
}]}
```
### 4. String Output
Raw strings are pushed directly:
```javascript
_output.push({tag: ["div", {}, false]}); // Opening tag (instruction)
_output.push("Hello, "); // Raw text
_output.push(html(this.data.name)); // Escaped text
_output.push("</div>"); // Closing tag (raw string!)
```
**CRITICAL**: Closing tags are ALWAYS raw strings, not instructions.
## Context Preservation
### v1 Pattern: `_that = this`
The v1 pattern preserves component context through nested functions:
```javascript
function render(Component, data, args, content) {
let _that = this; // Preserve component instance
let _output = [];
_output.push({comp: ["Child", {}, function(Child) {
let _output = [];
// _that still refers to parent component
_output.push(html(_that.data.value));
return [_output, _that];
}.bind(_that)]});
return [_output, _that];
}
```
### v2 Pattern: Arrow Functions
v2 uses arrow functions for natural context preservation:
```javascript
function render(Component, data, args, content) {
const _output = [];
_output.push({comp: ["Child", {}, (Child) => {
const _output = [];
// 'this' refers to parent component naturally
_output.push(html(this.data.value));
return [_output, this];
}]});
return [_output, this];
}
```
## Runtime Processing
### Phase 1: Instruction Execution
```javascript
let [instructions, context] = template.render.bind(component)(
component,
component.data,
args,
content_fn
);
```
### Phase 2: Instruction Processing
1. **Build HTML**: Process all string and simple tag instructions
2. **Track Complex Elements**: Store elements with function attributes
3. **Single DOM Write**: `component.$.html(html.join(''))`
4. **Apply Attributes**: Attach event handlers and complex attributes
5. **Initialize Components**: Create child component instances
## Self-Closing HTML Tags
The following HTML tags are automatically treated as self-closing:
- `area`, `base`, `br`, `col`, `embed`, `hr`, `img`, `input`
- `link`, `meta`, `param`, `source`, `track`, `wbr`
## Complete Example
### Template
```jqhtml
<Define:UserDashboard>
<div class="dashboard">
<Header $title="User Management">
<#actions>
<button $onclick=this.addUser>Add User</button>
</#actions>
</Header>
<% if (this.data.loading): %>
<LoadingSpinner />
<% else: %>
<UserTable $users=this.data.users>
<#header>
<th>ID</th>
<th>Name</th>
<th>Actions</th>
</#header>
<#row>
<td><%= row.id %></td>
<td><%= row.name %></td>
<td>
<button $onclick=this.editUser>Edit</button>
</td>
</#row>
</UserTable>
<% endif; %>
</div>
</Define:UserDashboard>
```
### Compiled Output Structure
```javascript
function render(Component, data, args, content) {
const _output = [];
_output.push({tag: ["div", {"class": "dashboard"}, false]});
// Header component with slots
_output.push({comp: ["Header", {"data-title": "User Management"}, (Header) => {
const _output = [];
_output.push({slot: ["actions", {}, (actions) => {
const _output = [];
_output.push({tag: ["button", {"data-onclick": this.addUser}, false]});
_output.push("Add User");
_output.push("</button>");
return [_output, this];
}]});
return [_output, this];
}]});
// ... rest of compilation
_output.push("</div>");
return [_output, this];
}
```
## Component CSS Classes
When components are rendered, the runtime automatically applies CSS classes based on the component's inheritance hierarchy:
```html
<!-- For a UserCard extending BaseCard extending Component -->
<div class="UserCard BaseCard Component" ...>
```
This enables CSS targeting at any level of the hierarchy:
```css
.Component { /* all components */ }
.UserCard { /* specific component type */ }
.BaseCard { /* all cards */ }
```
**Implementation Note**: The Component base class automatically applies these classes during initialization. This feature works regardless of minification since class names are preserved through the component's constructor name.
## Critical Implementation Rules
### What MUST Be Preserved
1. **Instruction Array Structure**: The `{tag:...}`, `{comp:...}`, `{slot:...}` format
2. **Return Tuple**: Functions MUST return `[instructions, context]`
3. **Deferred Component Init**: Components render as placeholders first
4. **Single DOM Write**: Build all HTML, write once
5. **Content Function**: Parent components receive content function to access slots
### Common Pitfalls
1. **Forgetting Return Format**: Must return `[array, context]`, not just array
2. **String vs Instruction**: Closing tags are strings, opening tags are instructions
3. **Context Loss**: Without proper binding, nested functions lose component reference
4. **Direct DOM Manipulation**: Never manipulate DOM during instruction building
5. **Missing html() Escaping**: User content must be escaped
6. **Slots Outside Components**: Slots must be inside component content functions

447
node_modules/@jqhtml/parser/README.md generated vendored Executable file
View File

@@ -0,0 +1,447 @@
# 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)
```jqhtml
<!-- 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
```jqhtml
<!-- 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:
```jqhtml
<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:**
```javascript
{
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:
```jqhtml
<!-- 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 content
- `EXPRESSION_START` - `<%=` opening tag
- `CODE_START` - `<%` opening tag
- `TAG_END` - `%>` closing tag
- `IF`, `ELSE`, `ELSEIF`, `ENDIF` - Conditional keywords
- `FOR`, `ENDFOR` - Loop keywords
- `DEFINE_START`, `DEFINE_END` - Component definition tags
- `COMPONENT_NAME` - Component identifier
- `JAVASCRIPT` - JavaScript code within tags
### Usage
```typescript
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
```bash
# 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:
1. **Text scanning** - Default mode, captures plain text
2. **Tag detection** - Looks for `<%`, `<%=`, `%>` sequences
3. **Keyword matching** - After `<%`, checks for control flow keywords
4. **JavaScript capture** - Captures code between tags
5. **Position tracking** - Updates line/column for every character
No regex patterns are used. All scanning is done with:
- `match_sequence()` - Check for exact string match
- `match_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 definitions
- `ComponentDefinition` - Component template definition
- `Text` - Plain text/HTML content
- `Expression` - JavaScript expressions (`<%= ... %>`)
- `IfStatement` - Conditional rendering with optional else
- `ForStatement` - Loop constructs
- `CodeBlock` - Generic JavaScript code blocks
### Usage
```typescript
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
```bash
# Run the parser test suite
node test-parser.js
# Run the interactive demo
node demo-parser.js
```
### Implementation Notes
The parser uses straightforward techniques:
1. **Token consumption** - Advances through tokens one at a time
2. **Lookahead** - Peeks at upcoming tokens to decide parsing path
3. **Context tracking** - Knows when inside components, loops, etc.
4. **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
```javascript
// 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
```typescript
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
```bash
# 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:
1. **Traverses the AST** - Visits each node and generates appropriate code
2. **Maintains context** - Tracks current jQuery element for appending
3. **Handles nesting** - For loops create temporary containers
4. **Strips syntax** - Removes trailing colons from control flow
5. **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](../../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 = this` pattern 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
1. **Lexer** - Full tokenization with position tracking
2. **Parser** - AST generation with all v2 syntax
3. **Code Generator** - Instruction array generation
4. **Component Integration** - Runtime integration complete
5. **Slot System** - `<#name>` syntax fully implemented
6. **Nested Components** - `<ComponentName>` recognition
7. **Binding Syntax** - `:prop` and `@event` support
### Current Features
- **Template Syntax**: HTML with embedded JavaScript
- **Control Flow**: `if/else/elseif` and `for` loops (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`, `:style` for data binding
- **Events**: `@click`, `@change`, etc. for event handlers
- **Scoped IDs**: `$id` attribute for component-scoped IDs
### Instruction Format
The parser generates v1-compatible instruction arrays:
```javascript
// 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]`:
```javascript
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
1. **Build Tools** - Webpack loader for `.jqhtml` imports
2. **Source Maps** - Full debugging support
3. **VS Code Extension** - Syntax highlighting
4. **Autoformatter** - Code formatting for `.jqhtml` files

218
node_modules/@jqhtml/parser/bin/jqhtml-compile generated vendored Executable file
View File

@@ -0,0 +1,218 @@
#!/usr/bin/env node
/**
* JQHTML Official CLI Compiler
*
* Compiles .jqhtml templates to JavaScript with multiple output formats
* Uses unified compiler module for single source of truth
*/
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { compileTemplate } from '../dist/index.js';
import { readFileSync } from 'fs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Get package version
const packagePath = path.join(path.dirname(__dirname), 'package.json');
const packageJson = JSON.parse(readFileSync(packagePath, 'utf-8'));
const VERSION = packageJson.version;
// Parse command line arguments
function parseArgs(argv) {
const args = {
input: null,
output: null,
format: 'iife',
sourcemap: false,
statusJson: false,
help: false,
version: false
};
for (let i = 2; i < argv.length; i++) {
const arg = argv[i];
if (arg === '--help' || arg === '-h') {
args.help = true;
} else if (arg === '--version' || arg === '-v') {
args.version = true;
} else if (arg === '--output' || arg === '-o') {
args.output = argv[++i];
if (!args.output) {
throw new Error('--output requires a file path');
}
} else if (arg === '--format') {
args.format = argv[++i];
if (!['iife', 'esm', 'cjs', 'umd'].includes(args.format)) {
throw new Error(`Invalid format: ${args.format}. Must be one of: iife, esm, cjs, umd`);
}
} else if (arg === '--sourcemap') {
args.sourcemap = true;
} else if (arg === '--status-json') {
args.statusJson = true;
} else if (!arg.startsWith('-')) {
args.input = arg;
}
}
return args;
}
// Show help message
function showHelp() {
console.log(`
JQHTML CLI Compiler v${VERSION}
Usage: jqhtml-compile <input.jqhtml> [options]
Options:
--output, -o <file> Write output to file instead of stdout
--format <format> Output format: iife|esm|cjs|umd (default: iife)
--sourcemap Include inline sourcemap
--status-json Output errors as JSON for programmatic parsing
--version, -v Show version number
--help, -h Show this help message
Examples:
jqhtml compile component.jqhtml
jqhtml compile component.jqhtml --output component.js
jqhtml compile component.jqhtml --format esm --sourcemap
jqhtml compile component.jqhtml -o dist/component.js --format umd
Output formats:
iife - Self-executing function that registers with window.jqhtml
esm - ES module export
cjs - CommonJS module.exports
umd - Universal module (works as AMD, CommonJS, or global)
`);
}
// Main compilation function
async function compile(inputFile, options) {
try {
// Read input file
const inputPath = path.resolve(inputFile);
if (!fs.existsSync(inputPath)) {
throw new Error(`Input file not found: ${inputPath}`);
}
const source = fs.readFileSync(inputPath, 'utf-8');
const filename = path.basename(inputPath);
// Use unified compiler
const compiled = compileTemplate(source, filename, {
format: options.format,
sourcemap: options.sourcemap,
version: VERSION
});
const formattedCode = compiled.code;
if (options.statusJson) {
// Output as JSON for programmatic consumption
const result = {
status: 'success',
result: formattedCode
};
if (options.output) {
result.outputFile = options.output;
}
console.log(JSON.stringify(result));
} else if (options.output) {
// Write to output file
const outputPath = path.resolve(options.output);
const outputDir = path.dirname(outputPath);
// Create output directory if it doesn't exist
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
fs.writeFileSync(outputPath, formattedCode, 'utf-8');
console.log(`✓ Compiled ${inputFile} → ${options.output}`);
} else {
// Output the compiled code to stdout without trailing newline
process.stdout.write(formattedCode);
}
return 0; // Success
} catch (error) {
if (options.statusJson) {
// Output error as JSON
const errorObj = {
status: 'error',
error: {
type: error.constructor.name,
message: error.message,
file: error.filename || null,
line: error.line || null,
column: error.column || null,
context: error.context || null,
suggestion: error.suggestion || null
}
};
console.log(JSON.stringify(errorObj));
} else {
// Output error to stderr
console.error(`Error: ${error.message}`);
if (error.line) {
console.error(` at line ${error.line}, column ${error.column || 0}`);
}
if (error.context) {
console.error(` context: ${error.context}`);
}
if (error.suggestion) {
console.error(` suggestion: ${error.suggestion}`);
}
}
return 1; // Error
}
}
// Main entry point
async function main() {
try {
const args = parseArgs(process.argv);
if (args.help) {
showHelp();
process.exit(0);
}
if (args.version) {
console.log(`v${VERSION}`);
process.exit(0);
}
if (!args.input) {
if (args.statusJson) {
console.log(JSON.stringify({
status: 'error',
error: {
type: 'ArgumentError',
message: 'No input file specified'
}
}));
} else {
console.error('Error: No input file specified');
console.error('Usage: jqhtml compile <input.jqhtml> [options]');
console.error('Use --help for more information');
}
process.exit(1);
}
const exitCode = await compile(args.input, args);
process.exit(exitCode);
} catch (error) {
console.error('Fatal error:', error.message);
process.exit(1);
}
}
// Run the CLI
main();

93
node_modules/@jqhtml/parser/dist/ast.d.ts generated vendored Executable file
View File

@@ -0,0 +1,93 @@
import { SourceLocation } from './lexer.js';
export declare enum NodeType {
PROGRAM = "Program",
COMPONENT_DEFINITION = "ComponentDefinition",
COMPONENT_INVOCATION = "ComponentInvocation",
HTML_TAG = "HtmlTag",
TEXT = "Text",
EXPRESSION = "Expression",
IF_STATEMENT = "IfStatement",
FOR_STATEMENT = "ForStatement",
CODE_BLOCK = "CodeBlock",
FRAGMENT = "Fragment",
SLOT = "Slot"
}
export interface BaseNode {
type: NodeType;
start: number;
end: number;
line: number;
column: number;
loc?: SourceLocation;
range?: [number, number];
}
export interface ProgramNode extends BaseNode {
type: NodeType.PROGRAM;
body: ASTNode[];
}
export interface ComponentDefinitionNode extends BaseNode {
type: NodeType.COMPONENT_DEFINITION;
name: string;
body: ASTNode[];
attributes: Record<string, any>;
extends?: string;
defineArgs?: Record<string, any>;
isSlotOnly?: boolean;
slotNames?: string[];
}
export interface TextNode extends BaseNode {
type: NodeType.TEXT;
content: string;
}
export interface ExpressionNode extends BaseNode {
type: NodeType.EXPRESSION;
code: string;
escaped: boolean;
}
export interface IfStatementNode extends BaseNode {
type: NodeType.IF_STATEMENT;
condition: string;
consequent: ASTNode[];
alternate: ASTNode[] | null;
}
export interface ForStatementNode extends BaseNode {
type: NodeType.FOR_STATEMENT;
iterator: string;
body: ASTNode[];
}
export interface CodeBlockNode extends BaseNode {
type: NodeType.CODE_BLOCK;
tokens?: Array<{
type: string;
value: string;
}>;
code?: string;
}
export interface FragmentNode extends BaseNode {
type: NodeType.FRAGMENT;
children: ASTNode[];
}
export interface SlotNode extends BaseNode {
type: NodeType.SLOT;
name: string;
attributes?: Record<string, any>;
children: ASTNode[];
selfClosing: boolean;
}
export interface ComponentInvocationNode extends BaseNode {
type: NodeType.COMPONENT_INVOCATION;
name: string;
attributes: Record<string, any>;
children: ASTNode[];
selfClosing: boolean;
}
export interface HtmlTagNode extends BaseNode {
type: NodeType.HTML_TAG;
name: string;
attributes: Record<string, any>;
children: ASTNode[];
selfClosing: boolean;
}
export type ASTNode = ProgramNode | ComponentDefinitionNode | ComponentInvocationNode | HtmlTagNode | TextNode | ExpressionNode | IfStatementNode | ForStatementNode | CodeBlockNode | FragmentNode | SlotNode;
export declare function createNode<T extends ASTNode>(type: T['type'], props: Omit<T, 'type' | keyof BaseNode>, start: number, end: number, line: number, column: number, loc?: SourceLocation): T;
//# sourceMappingURL=ast.d.ts.map

1
node_modules/@jqhtml/parser/dist/ast.d.ts.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"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"}

29
node_modules/@jqhtml/parser/dist/ast.js generated vendored Executable file
View File

@@ -0,0 +1,29 @@
// AST Node Types for JQHTML Templates
// Simple, clear node structures for the syntax tree
export var NodeType;
(function (NodeType) {
NodeType["PROGRAM"] = "Program";
NodeType["COMPONENT_DEFINITION"] = "ComponentDefinition";
NodeType["COMPONENT_INVOCATION"] = "ComponentInvocation";
NodeType["HTML_TAG"] = "HtmlTag";
NodeType["TEXT"] = "Text";
NodeType["EXPRESSION"] = "Expression";
NodeType["IF_STATEMENT"] = "IfStatement";
NodeType["FOR_STATEMENT"] = "ForStatement";
NodeType["CODE_BLOCK"] = "CodeBlock";
NodeType["FRAGMENT"] = "Fragment";
NodeType["SLOT"] = "Slot"; // v2 slot syntax
})(NodeType || (NodeType = {}));
// Helper to create nodes with common properties
export function createNode(type, props, start, end, line, column, loc) {
return {
type,
start,
end,
line,
column,
loc,
...props
};
}
//# sourceMappingURL=ast.js.map

1
node_modules/@jqhtml/parser/dist/ast.js.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"ast.js","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,oDAAoD;AAIpD,MAAM,CAAN,IAAY,QAYX;AAZD,WAAY,QAAQ;IAClB,+BAAmB,CAAA;IACnB,wDAA4C,CAAA;IAC5C,wDAA4C,CAAA;IAC5C,gCAAoB,CAAA;IACpB,yBAAa,CAAA;IACb,qCAAyB,CAAA;IACzB,wCAA4B,CAAA;IAC5B,0CAA8B,CAAA;IAC9B,oCAAwB,CAAA;IACxB,iCAAqB,CAAA;IACrB,yBAAa,CAAA,CAAE,iBAAiB;AAClC,CAAC,EAZW,QAAQ,KAAR,QAAQ,QAYnB;AAiHD,gDAAgD;AAChD,MAAM,UAAU,UAAU,CACxB,IAAe,EACf,KAAuC,EACvC,KAAa,EACb,GAAW,EACX,IAAY,EACZ,MAAc,EACd,GAAoB;IAEpB,OAAO;QACL,IAAI;QACJ,KAAK;QACL,GAAG;QACH,IAAI;QACJ,MAAM;QACN,GAAG;QACH,GAAG,KAAK;KACJ,CAAC;AACT,CAAC"}

97
node_modules/@jqhtml/parser/dist/codegen.d.ts generated vendored Executable file
View File

@@ -0,0 +1,97 @@
import { ProgramNode } from './ast.js';
export interface GeneratedCode {
code: string;
source_map?: string;
source_map_data_uri?: string;
components: Map<string, ComponentCode>;
}
export interface ComponentCode {
name: string;
render_function: string;
dependencies: string[];
tagName: string;
defaultAttributes: Record<string, any>;
defineArgs?: Record<string, any>;
extends?: string;
}
export declare class CodeGenerator {
private indent_level;
private components;
private current_component;
private in_slot;
private tag_depth;
private outputLine;
private outputColumn;
private sourceMapGenerator?;
private sourceContent?;
private sourceFile?;
private outputBuffer;
private enablePositionTracking;
private positionLog;
private currentOutputLine;
private preserveLines;
private outputLines;
private sourceLines;
generate(ast: ProgramNode, sourceFile?: string, sourceContent?: string): GeneratedCode;
/**
* Generate code with source maps using 1:1 line mapping + SourceMapGenerator
*/
generateWithSourceMap(ast: ProgramNode, sourceFile: string, sourceContent: string): GeneratedCode;
private generate_component_with_mappings_TESTING;
private generate_component;
private generate_function_body;
/**
* Generate function body with true 1:1 line mapping
* Each line in the source produces exactly one line in the output
*/
private generate_function_body_1to1;
private generate_node;
private generate_text;
private generate_expression;
private generate_if;
private generate_for;
private generate_code_block;
private generate_tag_open;
private generate_html_tag;
private generate_component_invocation;
private parse_attributes;
private generate_attributes_object;
private is_self_closing_tag;
private compile_interpolated_value;
private escape_string;
private indent;
private extract_slots_from_children;
/**
* Emit text and track position for source maps
*/
private emit;
/**
* Emit a line of text (adds newline)
*/
private emitLine;
/**
* Reset position tracking
*/
private resetPositionTracking;
/**
* Get position tracking log for debugging
*/
getPositionLog(): Array<{
line: number;
column: number;
text: string;
node?: string;
}>;
/**
* Enable/disable position tracking
*/
setPositionTracking(enabled: boolean): void;
/**
* Serialize attribute object with proper handling of identifiers and expressions
* Quoted values become strings, identifiers/expressions become raw JavaScript
*/
private serializeAttributeObject;
private build_module_code;
}
export declare function generate(ast: ProgramNode, sourceFile?: string, sourceContent?: string): GeneratedCode;
//# sourceMappingURL=codegen.d.ts.map

1
node_modules/@jqhtml/parser/dist/codegen.d.ts.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../src/codegen.ts"],"names":[],"mappings":"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"}

1230
node_modules/@jqhtml/parser/dist/codegen.js generated vendored Executable file

File diff suppressed because it is too large Load Diff

1
node_modules/@jqhtml/parser/dist/codegen.js.map generated vendored Executable file

File diff suppressed because one or more lines are too long

25
node_modules/@jqhtml/parser/dist/compiler.d.ts generated vendored Executable file
View File

@@ -0,0 +1,25 @@
/**
* Unified JQHTML Compiler Module
*
* Single source of truth for compiling JQHTML templates to JavaScript
* with proper sourcemap generation and version injection.
*/
export interface CompileOptions {
format: 'iife' | 'esm' | 'cjs' | 'umd';
sourcemap: boolean;
version?: string;
}
export interface CompiledOutput {
code: string;
componentName: string;
}
/**
* Compile a JQHTML template to JavaScript
*
* @param source - The JQHTML template source code
* @param filename - The source filename for sourcemap generation
* @param options - Compilation options
* @returns The compiled JavaScript code as a string
*/
export declare function compileTemplate(source: string, filename: string, options: CompileOptions): CompiledOutput;
//# sourceMappingURL=compiler.d.ts.map

1
node_modules/@jqhtml/parser/dist/compiler.d.ts.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../src/compiler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;IACvC,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,cAAc,GACtB,cAAc,CAmDhB"}

273
node_modules/@jqhtml/parser/dist/compiler.js generated vendored Executable file
View File

@@ -0,0 +1,273 @@
/**
* Unified JQHTML Compiler Module
*
* Single source of truth for compiling JQHTML templates to JavaScript
* with proper sourcemap generation and version injection.
*/
import { Lexer } from './lexer.js';
import { Parser } from './parser.js';
import { CodeGenerator } from './codegen.js';
/**
* Compile a JQHTML template to JavaScript
*
* @param source - The JQHTML template source code
* @param filename - The source filename for sourcemap generation
* @param options - Compilation options
* @returns The compiled JavaScript code as a string
*/
export function compileTemplate(source, filename, options) {
// Validate: content() must be called with <%= %> not <% %>
if (source.includes('<% content()')) {
throw new Error(`Invalid content() usage in ${filename}:\n\n` +
`content() must be output using <%= %> tags, not <% %> tags.\n\n` +
`Wrong: <% content() %>\n` +
`Right: <%= content() %>\n\n` +
`This ensures the content is properly rendered as output.`);
}
// 1. Parse the template
const lexer = new Lexer(source);
const tokens = lexer.tokenize();
const parser = new Parser(tokens, source, filename);
const ast = parser.parse();
// 2. Generate code WITHOUT sourcemap (we'll create it after wrapping)
const generator = new CodeGenerator();
const result = generator.generate(ast, filename, source);
// 3. Get component info
const componentName = result.components.keys().next().value;
if (!componentName) {
throw new Error('No component found in template');
}
const component = result.components.get(componentName);
if (!component) {
throw new Error(`Component ${componentName} not found in results`);
}
// 4. Apply format wrapping and version injection
const version = options.version || getPackageVersion();
let output = formatOutput(component, componentName, options.format, version);
// 5. Generate sourcemap AFTER wrapping (if requested)
if (options.sourcemap) {
const sourcemap = generateSourcemapForWrappedCode(output, source, filename);
output = output + '\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,' + sourcemap;
}
return {
code: output,
componentName
};
}
/**
* Get package version from package.json
* We can't use dynamic import in TypeScript, so we'll inject at build time
*/
function getPackageVersion() {
// This will be replaced by the build process
// The CLI will pass the version directly
return '__PARSER_VERSION__';
}
/**
* Serialize defineArgs/defaultAttributes with proper handling of identifiers and expressions
* Quoted values become strings, identifiers/expressions become raw JavaScript
*/
function serializeAttributeObject(obj) {
if (!obj || Object.keys(obj).length === 0) {
return '{}';
}
const entries = [];
for (const [key, value] of Object.entries(obj)) {
// Check if value is a parsed attribute object with type info
if (value && typeof value === 'object' && (value.identifier || value.expression)) {
// Identifier or expression - output as raw JavaScript (no quotes)
entries.push(`"${key}": ${value.value}`);
}
else if (value && typeof value === 'object' && value.quoted) {
// Quoted string - output as string literal
entries.push(`"${key}": ${JSON.stringify(value.value)}`);
}
else {
// Simple value - output as-is via JSON.stringify
entries.push(`"${key}": ${JSON.stringify(value)}`);
}
}
return `{${entries.join(', ')}}`;
}
/**
* Format the generated code according to the specified module format
* Moved from CLI compiler's formatOutput function
*/
function formatOutput(componentInfo, componentName, format, version) {
const name = componentInfo.name;
// Build the component definition
let componentDef = `{
_jqhtml_version: '${version}',
name: '${componentInfo.name}',
tag: '${componentInfo.tagName}',
defaultAttributes: ${serializeAttributeObject(componentInfo.defaultAttributes)},`;
// Add defineArgs if present ($ attributes from Define tag)
if (componentInfo.defineArgs) {
componentDef += `\n defineArgs: ${serializeAttributeObject(componentInfo.defineArgs)},`;
}
// Add extends if present (template inheritance)
if (componentInfo.extends) {
componentDef += `\n extends: '${componentInfo.extends}',`;
}
componentDef += `\n render: ${componentInfo.render_function.trimEnd()},
dependencies: ${JSON.stringify(componentInfo.dependencies)}
}`;
let output;
switch (format) {
case 'iife':
// Self-executing function that auto-registers with window.jqhtml
output = `// Compiled from: ${componentName}.jqhtml
(function() {
'use strict';
const template_${name} = ${componentDef};
// Self-register with jqhtml runtime
// Must use window.jqhtml since we're in bundle scope
if (!window.jqhtml) {
throw new Error('FATAL: window.jqhtml is not defined. The jqhtml runtime must be loaded before registering templates.');
}
// Auto-register following standard jqhtml pattern
window.jqhtml.register_template(template_${name});
})();`;
break;
case 'esm':
// ES Module export with auto-registration
output = `// ES Module: ${name}
import jqhtml from '@jqhtml/core';
const template_${name} = ${componentDef};
// Auto-register following standard jqhtml pattern
jqhtml.register_template(template_${name});
export { template_${name} };
export default template_${name};`;
break;
case 'cjs':
// CommonJS export with auto-registration
output = `// CommonJS Module: ${name}
'use strict';
const template_${name} = ${componentDef};
// Auto-register if jqhtml is available
if (typeof window !== 'undefined' && window.jqhtml) {
window.jqhtml.register_template(template_${name});
}
module.exports = template_${name};
module.exports.default = template_${name};
module.exports.template_${name} = template_${name};`;
break;
case 'umd':
// Universal Module Definition with auto-registration
output = `(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['@jqhtml/core'], factory);
} else if (typeof module === 'object' && module.exports) {
// CommonJS
module.exports = factory();
} else {
// Browser global
root.template_${name} = factory();
}
}(typeof self !== 'undefined' ? self : this, function (jqhtml) {
'use strict';
const template_${name} = ${componentDef};
// Auto-register with jqhtml runtime
if (typeof window !== 'undefined' && window.jqhtml) {
window.jqhtml.register_template(template_${name});
} else if (jqhtml) {
jqhtml.register_template(template_${name});
}
return template_${name};
}));`;
break;
default:
throw new Error(`Unknown format: ${format}`);
}
return output;
}
/**
* Generate a sourcemap for already-wrapped code
* This generates sourcemaps AFTER the wrapper has been applied
*/
function generateSourcemapForWrappedCode(wrappedCode, sourceContent, filename) {
// Count lines in wrapped output and source
const outputLines = wrappedCode.split('\n').length;
const sourceLines = sourceContent.split('\n').length;
// Find where the render function (template content) starts
const renderLineOffset = findRenderFunctionLine(wrappedCode);
if (renderLineOffset === 0) {
// Couldn't find render function, generate a basic mapping
console.warn('Could not find render function in wrapped output');
const mappings = new Array(outputLines).fill('AAAA').join(';');
const sourcemap = {
version: 3,
sources: [filename],
sourcesContent: [sourceContent],
mappings: mappings,
names: []
};
return Buffer.from(JSON.stringify(sourcemap)).toString('base64');
}
// Build mappings:
// 1. Lines before render content → all map to source line 1
// 2. Template content lines → map 1:1 to source lines
// 3. Lines after template content → all map to last source line
const mappings = [];
// Wrapper lines before template content
for (let i = 0; i < renderLineOffset - 1; i++) {
mappings.push('AAAA'); // Map to source line 1, column 0
}
// Template content lines (1:1 mapping)
// First line of template maps to source line 1
mappings.push('AAAA'); // Source line 1
// Remaining source lines map sequentially
for (let i = 1; i < sourceLines; i++) {
mappings.push('AACA'); // Each subsequent source line
}
// Any remaining wrapper lines after template content
const remainingLines = outputLines - mappings.length;
for (let i = 0; i < remainingLines; i++) {
mappings.push('AAAA'); // Map to last source line
}
// Create the sourcemap object
const sourcemap = {
version: 3,
sources: [filename],
sourcesContent: [sourceContent],
mappings: mappings.join(';'),
names: []
};
// Verify we have the right count
const finalCount = mappings.length;
if (finalCount !== outputLines) {
console.error(`Warning: Sourcemap line mismatch. Output has ${outputLines} lines, sourcemap has ${finalCount} mapping segments`);
}
return Buffer.from(JSON.stringify(sourcemap)).toString('base64');
}
/**
* Find the line number where template content starts in the wrapped output
* Returns 1-based line number
*/
function findRenderFunctionLine(outputCode) {
const lines = outputCode.split('\n');
for (let i = 0; i < lines.length; i++) {
// Look for the render function definition
if (lines[i].includes('render: function render(')) {
// Template content starts on the line AFTER the function declaration
// The function declaration ends with "{ let _output = []; ..."
// The next line is either empty or starts with template content
return i + 2; // i+1 for 1-based, +1 for next line after declaration
}
}
return 0; // Not found
}
//# sourceMappingURL=compiler.js.map

1
node_modules/@jqhtml/parser/dist/compiler.js.map generated vendored Executable file

File diff suppressed because one or more lines are too long

27
node_modules/@jqhtml/parser/dist/errors.d.ts generated vendored Executable file
View File

@@ -0,0 +1,27 @@
export declare class JQHTMLParseError extends Error {
line: number;
column: number;
endLine?: number;
endColumn?: number;
source?: string;
filename?: string;
severity: 'error' | 'warning';
suggestion?: string;
constructor(message: string, line: number, column: number, source?: string, filename?: string);
private buildErrorMessage;
private getCodeSnippet;
}
export declare function unclosedError(type: string, name: string, line: number, column: number, source?: string, filename?: string): JQHTMLParseError;
export declare function mismatchedTagError(opening: string, closing: string, line: number, column: number, source?: string, filename?: string): JQHTMLParseError;
export declare function syntaxError(message: string, line: number, column: number, source?: string, filename?: string): JQHTMLParseError;
export declare function getSuggestion(error: string): string;
export declare class ErrorCollector {
private errors;
private maxErrors;
constructor(maxErrors?: number);
add(error: JQHTMLParseError): void;
hasErrors(): boolean;
getErrors(): JQHTMLParseError[];
throwIfErrors(): void;
}
//# sourceMappingURL=errors.d.ts.map

1
node_modules/@jqhtml/parser/dist/errors.d.ts.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAGA,qBAAa,gBAAiB,SAAQ,KAAK;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAW;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;gBAGzB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM;IAgBnB,OAAO,CAAC,iBAAiB;IA0BzB,OAAO,CAAC,cAAc;CAiCvB;AAGD,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,GAChB,gBAAgB,CAQlB;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,GAChB,gBAAgB,CAQlB;AAED,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,GAChB,gBAAgB,CAQlB;AAGD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAiCnD;AAGD,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,SAAS,CAAc;gBAEnB,SAAS,GAAE,MAAW;IAIlC,GAAG,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI;IAMlC,SAAS,IAAI,OAAO;IAIpB,SAAS,IAAI,gBAAgB,EAAE;IAI/B,aAAa,IAAI,IAAI;CAmBtB"}

153
node_modules/@jqhtml/parser/dist/errors.js generated vendored Executable file
View File

@@ -0,0 +1,153 @@
// JQHTML Parser Error Classes
// Provides helpful error messages with code context
export class JQHTMLParseError extends Error {
line;
column;
endLine;
endColumn;
source;
filename;
severity = 'error';
suggestion;
constructor(message, line, column, source, filename) {
super(message);
this.name = 'JQHTMLParseError';
this.line = line;
this.column = column;
this.source = source;
this.filename = filename;
// Auto-add suggestions
this.suggestion = getSuggestion(message);
// Build the full error message with context
this.message = this.buildErrorMessage(message);
}
buildErrorMessage(message) {
let result = message;
// Add suggestion if available
if (this.suggestion) {
result += this.suggestion;
}
// Add location info
if (this.filename) {
result += `\n at ${this.filename}:${this.line}:${this.column}`;
}
else {
result += `\n at line ${this.line}, column ${this.column}`;
}
// Add code snippet if source is available
if (this.source) {
const snippet = this.getCodeSnippet();
if (snippet) {
result += '\n\n' + snippet;
}
}
return result;
}
getCodeSnippet() {
if (!this.source)
return '';
const lines = this.source.split('\n');
const lineIndex = this.line - 1;
// Show 3 lines before and after for better context
const contextLines = 3;
const startLine = Math.max(0, lineIndex - contextLines);
const endLine = Math.min(lines.length - 1, lineIndex + contextLines);
let snippet = '';
for (let i = startLine; i <= endLine; i++) {
const lineNum = i + 1;
const isErrorLine = i === lineIndex;
const prefix = isErrorLine ? '>' : ' ';
// Line number with padding
const lineNumStr = String(lineNum).padStart(5, ' ');
snippet += `${prefix} ${lineNumStr} | ${lines[i]}\n`;
// Add pointer to error column with better highlighting
if (isErrorLine) {
const spaces = ' '.repeat(this.column + 8);
const carets = '^'.repeat(Math.min(lines[i].length - this.column + 1, 20));
snippet += `${spaces}${carets}\n`;
}
}
return snippet;
}
}
// Common error factories
export function unclosedError(type, name, line, column, source, filename) {
return new JQHTMLParseError(`Unclosed ${type}: ${name}`, line, column, source, filename);
}
export function mismatchedTagError(opening, closing, line, column, source, filename) {
return new JQHTMLParseError(`Mismatched tags: expected </${opening}>, found </${closing}>`, line, column, source, filename);
}
export function syntaxError(message, line, column, source, filename) {
return new JQHTMLParseError(`Syntax error: ${message}`, line, column, source, filename);
}
// Helpful suggestions for common mistakes
export function getSuggestion(error) {
if (error.includes('Unclosed if statement')) {
return '\nDid you forget <% endif; %>?';
}
if (error.includes('Unclosed for statement')) {
return '\nDid you forget <% endfor; %>?';
}
if (error.includes('Unclosed component definition')) {
return '\nDid you forget the closing </Define:ComponentName> tag?';
}
if (error.includes('Unclosed slot')) {
return '\nDid you mean to use a self-closing slot? Try <#name /> instead.';
}
if (error.includes('Unclosed tag') || error.includes('Unclosed component')) {
return '\n\nThis element was opened but never closed. Make sure every opening tag has a matching closing tag.\n' +
'Common causes:\n' +
' • Missing closing tag (e.g., forgot </div>)\n' +
' • Tag opened in if/for block but closed outside (split tag conditional - not allowed)\n' +
' • Mismatched nesting (e.g., <div><span></div></span>)\n\n' +
'If opened in <% if/for %>, it MUST close in the same block:\n' +
' ❌ <% if (x) { %> <div> <% } %> </div>\n' +
' ✅ <% if (x) { %> <div>...</div> <% } %>';
}
if (error.includes('Expected %>')) {
return '\nCheck if you have a %> inside a string literal. Escape it or use a different approach.';
}
if (error.includes('Mismatched tags')) {
return '\nCheck that your opening and closing tags match exactly (case-sensitive).';
}
if (error.includes('Mixed content not allowed')) {
return '\nWhen using slots, wrap all content in <#slotname> tags. Use <#default> for the main content.';
}
return '';
}
// Error collection for batch reporting
export class ErrorCollector {
errors = [];
maxErrors = 10;
constructor(maxErrors = 10) {
this.maxErrors = maxErrors;
}
add(error) {
if (this.errors.length < this.maxErrors) {
this.errors.push(error);
}
}
hasErrors() {
return this.errors.length > 0;
}
getErrors() {
return this.errors;
}
throwIfErrors() {
if (this.errors.length === 0)
return;
if (this.errors.length === 1) {
throw this.errors[0];
}
// Multiple errors - create a combined message
let message = `Found ${this.errors.length} errors:\n\n`;
this.errors.forEach((error, index) => {
message += `Error ${index + 1}: ${error.message}\n`;
if (index < this.errors.length - 1) {
message += '\n';
}
});
throw new Error(message);
}
}
//# sourceMappingURL=errors.js.map

1
node_modules/@jqhtml/parser/dist/errors.js.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}

8
node_modules/@jqhtml/parser/dist/index.d.ts generated vendored Executable file
View File

@@ -0,0 +1,8 @@
export { Lexer, Token, TokenType } from './lexer.js';
export { Parser } from './parser.js';
export { CodeGenerator, generate } from './codegen.js';
export { compileTemplate, CompileOptions, CompiledOutput } from './compiler.js';
export { NodeType, ASTNode, ProgramNode, ComponentDefinitionNode, TextNode, ExpressionNode, IfStatementNode, ForStatementNode, CodeBlockNode, FragmentNode } from './ast.js';
export { JQHTMLParseError, ErrorCollector } from './errors.js';
export declare function parse(source: string, filename?: string): import("./ast.js").ProgramNode;
//# sourceMappingURL=index.d.ts.map

1
node_modules/@jqhtml/parser/dist/index.d.ts.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EACL,QAAQ,EACR,OAAO,EACP,WAAW,EACX,uBAAuB,EACvB,QAAQ,EACR,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,YAAY,EACb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG/D,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,kCAoBtD"}

31
node_modules/@jqhtml/parser/dist/index.js generated vendored Executable file
View File

@@ -0,0 +1,31 @@
// JQHTML Parser - Main entry point
import { Lexer } from './lexer.js';
import { Parser } from './parser.js';
export { Lexer, TokenType } from './lexer.js';
export { Parser } from './parser.js';
export { CodeGenerator, generate } from './codegen.js';
export { compileTemplate } from './compiler.js';
export { NodeType } from './ast.js';
export { JQHTMLParseError, ErrorCollector } from './errors.js';
// Convenience function for parsing with error context
export function parse(source, filename) {
try {
const lexer = new Lexer(source);
const tokens = lexer.tokenize();
const parser = new Parser(tokens, source, filename);
return parser.parse();
}
catch (error) {
// If it's already a JQHTMLParseError, just re-throw it
if (error.name === 'JQHTMLParseError') {
throw error;
}
// Otherwise wrap it with context
const message = error.message || String(error);
const enhancedMessage = filename
? `Error parsing ${filename}: ${message}`
: `Error parsing JQHTML: ${message}`;
throw new Error(enhancedMessage);
}
}
//# sourceMappingURL=index.js.map

1
node_modules/@jqhtml/parser/dist/index.js.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mCAAmC;AAEnC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAE,KAAK,EAAS,SAAS,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,eAAe,EAAkC,MAAM,eAAe,CAAC;AAChF,OAAO,EACL,QAAQ,EAUT,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE/D,sDAAsD;AACtD,MAAM,UAAU,KAAK,CAAC,MAAc,EAAE,QAAiB;IACrD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC;QAChC,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QACpD,OAAO,MAAM,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,uDAAuD;QACvD,IAAI,KAAK,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;YACtC,MAAM,KAAK,CAAC;QACd,CAAC;QAED,iCAAiC;QACjC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC;QAC/C,MAAM,eAAe,GAAG,QAAQ;YAC9B,CAAC,CAAC,iBAAiB,QAAQ,KAAK,OAAO,EAAE;YACzC,CAAC,CAAC,yBAAyB,OAAO,EAAE,CAAC;QAEvC,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACnC,CAAC;AACH,CAAC"}

3
node_modules/@jqhtml/parser/dist/integration.d.ts generated vendored Executable file
View File

@@ -0,0 +1,3 @@
export declare function with_template(ComponentClass: any, template_name: string): any;
export declare function register_compiled_templates(template_map: Map<string, any>): void;
//# sourceMappingURL=integration.d.ts.map

1
node_modules/@jqhtml/parser/dist/integration.d.ts.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../src/integration.ts"],"names":[],"mappings":"AAkBA,wBAAgB,aAAa,CAAC,cAAc,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,OA8BvE;AAGD,wBAAgB,2BAA2B,CAAC,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,QASzE"}

47
node_modules/@jqhtml/parser/dist/integration.js generated vendored Executable file
View File

@@ -0,0 +1,47 @@
// JQHTML v2 Template Integration
// Connects compiled templates to Component class
/// <reference path="./types.d.ts" />
import { process_instructions, html, register_template } from './runtime.js';
// Mixin to add template support to any component
export function with_template(ComponentClass, template_name) {
const original_on_render = ComponentClass.prototype.on_render;
ComponentClass.prototype.on_render = async function () {
// Get compiled template from registry
const global_obj = typeof window !== 'undefined' ? window : global;
const template_map = global_obj.jqhtml_components;
if (!template_map || !template_map.has(template_name)) {
throw new Error(`Template not found: ${template_name}`);
}
const template_info = template_map.get(template_name);
const render_fn = template_info.render;
// Call render function with component context
const [instructions] = render_fn.call(this, this.constructor, this.data, this.args, {});
// Process instructions into DOM
const rendered = process_instructions(instructions, this);
// Clear and append to component element
this.$.empty().append(rendered);
// Call original on_render if it exists
if (original_on_render) {
await original_on_render.call(this);
}
};
return ComponentClass;
}
// Register templates from compiled module
export function register_compiled_templates(template_map) {
// Store on global object for now (MVP approach)
const global_obj = typeof window !== 'undefined' ? window : global;
global_obj.jqhtml_components = template_map;
// Also register each template
for (const [name, info] of template_map) {
register_template(name, info.render);
}
}
// Make html function available globally for templates
if (typeof window !== 'undefined') {
window.html = html;
}
else if (typeof global !== 'undefined') {
global.html = html;
}
//# sourceMappingURL=integration.js.map

1
node_modules/@jqhtml/parser/dist/integration.js.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"integration.js","sourceRoot":"","sources":["../src/integration.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,iDAAiD;AAEjD,qCAAqC;AAErC,OAAO,EAAE,oBAAoB,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAY7E,iDAAiD;AACjD,MAAM,UAAU,aAAa,CAAC,cAAmB,EAAE,aAAqB;IACtE,MAAM,kBAAkB,GAAG,cAAc,CAAC,SAAS,CAAC,SAAS,CAAC;IAE9D,cAAc,CAAC,SAAS,CAAC,SAAS,GAAG,KAAK;QACxC,sCAAsC;QACtC,MAAM,UAAU,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;QACnE,MAAM,YAAY,GAAI,UAAkB,CAAC,iBAAiB,CAAC;QAC3D,IAAI,CAAC,YAAY,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC;YACtD,MAAM,IAAI,KAAK,CAAC,uBAAuB,aAAa,EAAE,CAAC,CAAC;QAC1D,CAAC;QAED,MAAM,aAAa,GAAG,YAAY,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QACtD,MAAM,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC;QAEvC,8CAA8C;QAC9C,MAAM,CAAC,YAAY,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAExF,gCAAgC;QAChC,MAAM,QAAQ,GAAG,oBAAoB,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;QAE1D,wCAAwC;QACxC,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAEhC,uCAAuC;QACvC,IAAI,kBAAkB,EAAE,CAAC;YACvB,MAAM,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,cAAc,CAAC;AACxB,CAAC;AAED,0CAA0C;AAC1C,MAAM,UAAU,2BAA2B,CAAC,YAA8B;IACxE,gDAAgD;IAChD,MAAM,UAAU,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IAClE,UAAkB,CAAC,iBAAiB,GAAG,YAAY,CAAC;IAErD,8BAA8B;IAC9B,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,YAAY,EAAE,CAAC;QACxC,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;AACH,CAAC;AAED,sDAAsD;AACtD,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;IAClC,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;AACrB,CAAC;KAAM,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;IACxC,MAAc,CAAC,IAAI,GAAG,IAAI,CAAC;AAC9B,CAAC"}

130
node_modules/@jqhtml/parser/dist/lexer.d.ts generated vendored Executable file
View File

@@ -0,0 +1,130 @@
export declare enum TokenType {
TEXT = "TEXT",
EXPRESSION_START = "EXPRESSION_START",// <%=
EXPRESSION_UNESCAPED = "EXPRESSION_UNESCAPED",// <%!=
CODE_START = "CODE_START",// <%
TAG_END = "TAG_END",// %>
IF = "IF",
ELSE = "ELSE",
ELSEIF = "ELSEIF",
ENDIF = "ENDIF",
FOR = "FOR",
ENDFOR = "ENDFOR",
COMMENT = "COMMENT",// <%-- comment --%>
DEFINE_START = "DEFINE_START",// <Define:
DEFINE_END = "DEFINE_END",// </Define:
COMPONENT_NAME = "COMPONENT_NAME",
SLOT_START = "SLOT_START",// <#
SLOT_END = "SLOT_END",// </#
SLOT_NAME = "SLOT_NAME",
TAG_OPEN = "TAG_OPEN",// <tagname or <ComponentName
TAG_CLOSE = "TAG_CLOSE",// </tagname or </ComponentName
TAG_NAME = "TAG_NAME",// The tag/component name
SELF_CLOSING = "SELF_CLOSING",// />
ATTR_NAME = "ATTR_NAME",// $property or regular
ATTR_VALUE = "ATTR_VALUE",
COLON = "COLON",
SEMICOLON = "SEMICOLON",
GT = "GT",// >
LT = "LT",// <
SLASH = "SLASH",// /
EQUALS = "EQUALS",// =
QUOTE = "QUOTE",// " or '
EOF = "EOF",
NEWLINE = "NEWLINE",
WHITESPACE = "WHITESPACE",
JAVASCRIPT = "JAVASCRIPT"
}
export interface SourceLocation {
start: {
line: number;
column: number;
offset: number;
};
end: {
line: number;
column: number;
offset: number;
};
}
export interface Token {
type: TokenType;
value: string;
line: number;
column: number;
start: number;
end: number;
loc?: SourceLocation;
}
export declare class Lexer {
private input;
private position;
private line;
private column;
private tokens;
private savedPosition;
constructor(input: string);
/**
* Save current position for later token creation
*/
private savePosition;
/**
* Get saved position or current position
*/
private getSavedPosition;
/**
* Replace <%-- comment --%> with equivalent number of newlines
* This ensures line mapping stays accurate while removing comment content
*/
private preprocessComments;
/**
* Replace HTML comments (<!-- -->) that appear OUTSIDE of <Define> tags
* This strips documentation comments before component definitions
* HTML comments INSIDE <Define> tags are preserved in the output
*/
private preprocessHTMLComments;
/**
* Preprocess code blocks and expressions
* - Insert comment markers for empty lines in code blocks
* - Collapse multi-line expressions to single line with trailing newlines
* This ensures 1:1 line mapping in generated code
*/
private preprocessCodeBlocks;
/**
* Find the closing %> tag, properly handling strings and comments
*/
private findClosingTag;
tokenize(): Token[];
private scan_next;
private scan_text;
private scan_code_block;
private scan_comment;
private scan_html_comment;
private scan_expression;
private scan_javascript;
private scan_component_name;
private scan_slot_name;
private match_sequence;
private match_keyword;
private peek_sequence;
private peek_sequence_at;
private skip_whitespace;
private current_char;
private peek_ahead;
private advance;
private add_token;
private is_tag_name_char;
private is_tag_name_continue_char;
private scan_opening_tag;
private scan_closing_tag;
private scan_attributes;
private is_attribute_start_char;
private peek_for_colon;
private scan_attribute;
private scan_attribute_value;
private validate_unquoted_value;
private get_current_attribute_context;
private value_contains_interpolation;
private scan_interpolated_attribute_value;
}
//# sourceMappingURL=lexer.d.ts.map

1
node_modules/@jqhtml/parser/dist/lexer.d.ts.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"lexer.d.ts","sourceRoot":"","sources":["../src/lexer.ts"],"names":[],"mappings":"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"}

1384
node_modules/@jqhtml/parser/dist/lexer.js generated vendored Executable file

File diff suppressed because it is too large Load Diff

1
node_modules/@jqhtml/parser/dist/lexer.js.map generated vendored Executable file

File diff suppressed because one or more lines are too long

53
node_modules/@jqhtml/parser/dist/parser.d.ts generated vendored Executable file
View File

@@ -0,0 +1,53 @@
import { Token } from './lexer.js';
import { ProgramNode } from './ast.js';
export declare class Parser {
private tokens;
private current;
private source?;
private filename?;
private static readonly VOID_ELEMENTS;
constructor(tokens: Token[], source?: string, filename?: string);
parse(): ProgramNode;
private parse_top_level;
private parse_component_definition;
private parse_content;
private parse_expression;
private parse_code_block;
private parse_if_statement;
private parse_for_statement;
private static readonly JAVASCRIPT_RESERVED_WORDS;
private parse_slot;
private parse_tag;
private parse_attributes;
private parse_attribute_value;
private check_closing_tag;
private match;
private check;
private check_ahead;
private check_sequence;
private advance;
private is_at_end;
private peek;
private peek_ahead;
private previous;
private current_token;
private previous_token;
/**
* Create a SourceLocation from start and end tokens
* Propagates loc field if available, falls back to old fields for compatibility
*/
private create_location;
private consume;
private validate_component_children;
/**
* Compile method for simplified API
* Parses the template and returns component metadata and render function
*/
compile(): {
name: string;
tagName: string;
defaultAttributes: Record<string, any>;
renderFunction: string;
};
}
//# sourceMappingURL=parser.d.ts.map

1
node_modules/@jqhtml/parser/dist/parser.d.ts.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAA6B,MAAM,YAAY,CAAC;AAC9D,OAAO,EAGL,WAAW,EASZ,MAAM,UAAU,CAAC;AAUlB,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,CAAS;IAI1B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAGlC;gBAES,MAAM,EAAE,KAAK,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;IAO/D,KAAK,IAAI,WAAW;IA0EpB,OAAO,CAAC,eAAe;IAgBvB,OAAO,CAAC,0BAA0B;IAqNlC,OAAO,CAAC,aAAa;IA6DrB,OAAO,CAAC,gBAAgB;IAoBxB,OAAO,CAAC,gBAAgB;IAmCxB,OAAO,CAAC,kBAAkB;IA+G1B,OAAO,CAAC,mBAAmB;IAsD3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,yBAAyB,CAW9C;IAGH,OAAO,CAAC,UAAU;IAmGlB,OAAO,CAAC,SAAS;IAsMjB,OAAO,CAAC,gBAAgB;IAyDxB,OAAO,CAAC,qBAAqB;IA+D7B,OAAO,CAAC,iBAAiB;IAgBzB,OAAO,CAAC,KAAK;IAUb,OAAO,CAAC,KAAK;IAKb,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,OAAO;IAKf,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,cAAc;IAItB;;;OAGG;IACH,OAAO,CAAC,eAAe;IAYvB,OAAO,CAAC,OAAO;IA4Cf,OAAO,CAAC,2BAA2B;IA+BnC;;;OAGG;IACH,OAAO,IAAI;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE;CAkC7G"}

838
node_modules/@jqhtml/parser/dist/parser.js generated vendored Executable file
View File

@@ -0,0 +1,838 @@
// JQHTML Parser - Builds AST from tokens
// Simple recursive descent parser, no complex libraries
import { TokenType } from './lexer.js';
import { NodeType, createNode } from './ast.js';
import { unclosedError, mismatchedTagError, syntaxError, getSuggestion } from './errors.js';
import { CodeGenerator } from './codegen.js';
export class Parser {
tokens;
current = 0;
source;
filename;
// HTML5 void elements that cannot have closing tags
// These are automatically treated as self-closing
static VOID_ELEMENTS = new Set([
'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',
'link', 'meta', 'source', 'track', 'wbr'
]);
constructor(tokens, source, filename) {
this.tokens = tokens;
this.source = source;
this.filename = filename;
}
// Main entry point - parse tokens into AST
parse() {
const body = [];
const start = this.current_token();
// Skip leading whitespace and newlines
while (!this.is_at_end() && (this.match(TokenType.NEWLINE) ||
(this.match(TokenType.TEXT) && this.previous_token().value.trim() === ''))) {
// Skip whitespace
}
// Check if file is empty (only whitespace)
if (this.is_at_end()) {
// Empty file is allowed
return createNode(NodeType.PROGRAM, { body: [] }, start.start, start.end, start.line, start.column, this.create_location(start, start));
}
// Must have exactly one Define tag at top level
if (!this.check(TokenType.DEFINE_START)) {
const token = this.current_token();
throw syntaxError('JQHTML files must have exactly one top-level <Define:ComponentName> tag', token.line, token.column, this.source, this.filename);
}
// Parse the single component definition
const component = this.parse_component_definition();
if (component) {
body.push(component);
}
// Skip trailing whitespace and newlines
while (!this.is_at_end() && (this.match(TokenType.NEWLINE) || this.match(TokenType.TEXT) && this.previous_token().value.trim() === '')) {
// Skip whitespace
}
// Ensure no other content after the Define tag
if (!this.is_at_end()) {
const token = this.current_token();
throw syntaxError('JQHTML files must have exactly one top-level <Define:ComponentName> tag. Found additional content after the component definition.', token.line, token.column, this.source, this.filename);
}
const end = this.previous_token();
return createNode(NodeType.PROGRAM, { body }, start.start, end.end, start.line, start.column, this.create_location(start, end));
}
// Parse top-level constructs
parse_top_level() {
// Skip whitespace-only text nodes at top level
if (this.match(TokenType.NEWLINE)) {
return null;
}
// Component definition
if (this.check(TokenType.DEFINE_START)) {
return this.parse_component_definition();
}
// Regular content
return this.parse_content();
}
// Parse <Define:ComponentName>...</Define:ComponentName>
parse_component_definition() {
const start_token = this.consume(TokenType.DEFINE_START, 'Expected <Define:');
const name_token = this.consume(TokenType.COMPONENT_NAME, 'Expected component name');
// Validate component name starts with capital letter
if (!/^[A-Z]/.test(name_token.value)) {
throw syntaxError(`Component name '${name_token.value}' must start with a capital letter. Convention is First_Letter_With_Underscores.`, name_token.line, name_token.column, this.source, this.filename);
}
// Parse attributes (like tag="span", class="card", extends="Parent", $prop=value)
const attributes = {};
const defineArgs = {}; // $ attributes (raw JS, not data- attributes)
let extendsValue;
// Skip any leading newlines before attributes
while (this.match(TokenType.NEWLINE)) {
// Skip
}
while (!this.check(TokenType.GT) && !this.is_at_end()) {
const attr_name = this.consume(TokenType.ATTR_NAME, 'Expected attribute name');
// Validate that $id is not used in Define tags
if (attr_name.value === '$id') {
throw syntaxError('$id is not allowed in <Define:> tags. Component definitions cannot have scoped IDs.', attr_name.line, attr_name.column, this.source, this.filename);
}
this.consume(TokenType.EQUALS, 'Expected =');
const attr_value = this.parse_attribute_value();
// Check if attribute value contains template expressions (dynamic content)
// Define tag attributes must be static - they're part of the template definition, not runtime values
const hasInterpolation = attr_value &&
typeof attr_value === 'object' &&
(attr_value.interpolated === true ||
(attr_value.parts && attr_value.parts.some((p) => p.type === 'expression')));
if (hasInterpolation) {
// Special error message for class attribute - most common case
if (attr_name.value === 'class') {
const error = syntaxError(`Template expressions cannot be used in <Define:> tag attributes. The <Define> tag is a static template definition, not a live component instance.`, attr_name.line, attr_name.column, this.source, this.filename);
error.message += '\n\n' +
' For dynamic classes, set the class attribute on the component invocation instead:\n' +
' ❌ <Define:MyComponent class="<%= this.args.col_class || \'default\' %>">\n' +
' ✅ <Define:MyComponent class="static-class">\n' +
' ...\n' +
' </Define:MyComponent>\n\n' +
' Then when using the component:\n' +
' <MyComponent class="col-md-8 col-xl-4" />\n\n' +
' Or set attributes dynamically in on_create() or on_ready():\n' +
' on_ready() {\n' +
' const classes = this.args.col_class || \'col-12 col-md-6\';\n' +
' this.$.addClass(classes);\n' +
' }';
throw error;
}
else {
// General error for other attributes
const error = syntaxError(`Template expressions cannot be used in <Define:> tag attributes. The <Define> tag is a static template definition, not a live component instance.`, attr_name.line, attr_name.column, this.source, this.filename);
error.message += '\n\n' +
` For dynamic "${attr_name.value}" attribute values, set them dynamically in lifecycle methods:\n` +
' ❌ <Define:MyComponent ' + attr_name.value + '="<%= expression %>">\n' +
' ✅ <Define:MyComponent>\n' +
' ...\n' +
' </Define:MyComponent>\n\n' +
' Then in your component class:\n' +
' on_create() {\n' +
` this.$.attr('${attr_name.value}', this.args.some_value);\n` +
' }\n\n' +
' Or use on_ready() for attributes that need DOM to be fully initialized.';
throw error;
}
}
// Handle special attributes on Define tags
if (attr_name.value === 'extends') {
// extends="ParentComponent" - explicit template inheritance
if (typeof attr_value === 'object' && attr_value.quoted) {
extendsValue = attr_value.value;
}
else if (typeof attr_value === 'string') {
extendsValue = attr_value;
}
else {
throw syntaxError(`extends attribute must be a quoted string with the parent component name`, attr_name.line, attr_name.column, this.source, this.filename);
}
}
else if (attr_name.value.startsWith('$')) {
// $ attributes on Define tags are raw JS assignments (like component invocations)
// Store them separately - they don't become data- attributes
const propName = attr_name.value.substring(1); // Remove $
defineArgs[propName] = attr_value;
}
else {
// Regular attributes (tag="span", class="card", etc.)
attributes[attr_name.value] = attr_value;
}
// Skip newlines between attributes
while (this.match(TokenType.NEWLINE)) {
// Skip
}
}
this.consume(TokenType.GT, 'Expected >');
const body = [];
// Parse until we find the closing tag
while (!this.check(TokenType.DEFINE_END)) {
if (this.is_at_end()) {
const error = unclosedError('component definition', name_token.value, name_token.line, name_token.column, this.source, this.filename);
error.message += getSuggestion(error.message);
throw error;
}
const node = this.parse_content();
if (node) {
body.push(node);
}
}
// Consume closing tag
this.consume(TokenType.DEFINE_END, 'Expected </Define:');
const closing_name = this.consume(TokenType.COMPONENT_NAME, 'Expected component name');
if (closing_name.value !== name_token.value) {
throw mismatchedTagError(`Define:${name_token.value}`, `Define:${closing_name.value}`, closing_name.line, closing_name.column, this.source, this.filename);
}
const end_token = this.consume(TokenType.GT, 'Expected >');
// Detect slot-only templates for inheritance
let isSlotOnly = false;
let slotNames = [];
// Check if body contains only slot nodes (ignoring whitespace-only text)
const nonWhitespaceNodes = body.filter(node => {
if (node.type === NodeType.TEXT) {
return node.content.trim() !== '';
}
return true;
});
if (nonWhitespaceNodes.length > 0) {
// Check if ALL non-whitespace nodes are slots
const allSlots = nonWhitespaceNodes.every(node => node.type === NodeType.SLOT);
if (allSlots) {
isSlotOnly = true;
slotNames = nonWhitespaceNodes.map(node => node.name);
}
}
return createNode(NodeType.COMPONENT_DEFINITION, {
name: name_token.value,
body,
attributes,
extends: extendsValue,
defineArgs: Object.keys(defineArgs).length > 0 ? defineArgs : undefined,
isSlotOnly,
slotNames
}, start_token.start, end_token.end, start_token.line, start_token.column, this.create_location(start_token, end_token));
}
// Parse content (text, expressions, control flow)
parse_content() {
// Comments are now preprocessed into whitespace by the lexer
// Plain text
if (this.match(TokenType.TEXT)) {
const token = this.previous();
return createNode(NodeType.TEXT, { content: token.value }, token.start, token.end, token.line, token.column, this.create_location(token, token));
}
// Expression <%= ... %> or <%!= ... %>
if (this.match(TokenType.EXPRESSION_START) ||
this.match(TokenType.EXPRESSION_UNESCAPED)) {
return this.parse_expression();
}
// Code block <% ... %>
if (this.match(TokenType.CODE_START)) {
return this.parse_code_block();
}
// Slot <#name>...</#name>
if (this.match(TokenType.SLOT_START)) {
return this.parse_slot();
}
// HTML tags and component invocations
if (this.match(TokenType.TAG_OPEN)) {
return this.parse_tag();
}
// Skip newlines in content
if (this.match(TokenType.NEWLINE)) {
const token = this.previous();
return createNode(NodeType.TEXT, { content: token.value }, token.start, token.end, token.line, token.column, this.create_location(token, token));
}
// Advance if we don't recognize the token
if (!this.is_at_end()) {
this.advance();
}
return null;
}
// Parse <%= expression %> or <%!= expression %>
parse_expression() {
const start_token = this.previous(); // EXPRESSION_START or EXPRESSION_UNESCAPED
const code_token = this.consume(TokenType.JAVASCRIPT, 'Expected JavaScript code');
const end_token = this.consume(TokenType.TAG_END, 'Expected %>');
return createNode(NodeType.EXPRESSION, {
code: code_token.value,
escaped: start_token.type === TokenType.EXPRESSION_START
}, start_token.start, end_token.end, start_token.line, start_token.column, this.create_location(start_token, end_token));
}
// Parse <% code %> - collect tokens with their types for proper transformation
parse_code_block() {
const start_token = this.previous(); // CODE_START
// Collect tokens with their types
const tokens = [];
while (!this.check(TokenType.TAG_END)) {
if (this.is_at_end()) {
throw syntaxError('Unterminated code block - expected %>', start_token.line, start_token.column, this.source, this.filename);
}
const token = this.advance();
tokens.push({ type: token.type, value: token.value });
}
const end_token = this.consume(TokenType.TAG_END, 'Expected %>');
return createNode(NodeType.CODE_BLOCK, { tokens }, // Pass tokens array instead of concatenated code
start_token.start, end_token.end, start_token.line, start_token.column, this.create_location(start_token, end_token));
}
// Parse if statement with colon style only
parse_if_statement(start_token) {
console.log('[Parser] Parsing if statement at line', start_token.line);
this.consume(TokenType.IF, 'Expected if');
const condition_token = this.consume(TokenType.JAVASCRIPT, 'Expected condition');
console.log('[Parser] If condition:', condition_token.value);
this.consume(TokenType.TAG_END, 'Expected %>');
const consequent = [];
let alternate = null;
// Parse consequent branch
while (!this.check_sequence(TokenType.CODE_START, TokenType.ELSE) &&
!this.check_sequence(TokenType.CODE_START, TokenType.ENDIF)) {
if (this.is_at_end()) {
const error = unclosedError('if statement', `if (${condition_token.value})`, start_token.line, start_token.column, this.source, this.filename);
error.message += getSuggestion(error.message);
throw error;
}
const node = this.parse_content();
if (node) {
consequent.push(node);
}
}
// Check for else branch
if (this.check_sequence(TokenType.CODE_START, TokenType.ELSE)) {
console.log('[Parser] Found else branch');
this.advance(); // CODE_START
this.advance(); // ELSE
// Check if this is an "else if"
if (this.check(TokenType.IF)) {
console.log('[Parser] This is an else if statement');
// This is an else if - put back the ELSE token and parse as new if statement
this.current--; // Put back ELSE
this.current--; // Put back CODE_START
// Parse the else if as a new if statement
alternate = [];
const elseIfNode = this.parse_content();
if (elseIfNode) {
alternate.push(elseIfNode);
}
}
else {
// Regular else branch
// Skip optional trailing code
if (this.check(TokenType.JAVASCRIPT)) {
this.advance();
}
this.consume(TokenType.TAG_END, 'Expected %>');
alternate = [];
// Parse else branch
while (!this.check_sequence(TokenType.CODE_START, TokenType.ENDIF)) {
if (this.is_at_end()) {
const error = unclosedError('if statement (in else branch)', `if (${condition_token.value})`, start_token.line, start_token.column, this.source, this.filename);
error.message += getSuggestion(error.message);
throw error;
}
const node = this.parse_content();
if (node) {
alternate.push(node);
}
}
}
}
// Consume endif
this.consume(TokenType.CODE_START, 'Expected <%');
this.consume(TokenType.ENDIF, 'Expected endif');
// Skip optional semicolon
if (this.check(TokenType.JAVASCRIPT)) {
this.advance();
}
const end_token = this.consume(TokenType.TAG_END, 'Expected %>');
return createNode(NodeType.IF_STATEMENT, {
condition: condition_token.value,
consequent,
alternate
}, start_token.start, end_token.end, start_token.line, start_token.column, this.create_location(start_token, end_token));
}
// Parse for loop
parse_for_statement(start_token) {
this.consume(TokenType.FOR, 'Expected for');
const iterator_token = this.consume(TokenType.JAVASCRIPT, 'Expected iterator expression');
this.consume(TokenType.TAG_END, 'Expected %>');
const body = [];
// Parse loop body
while (!this.check_sequence(TokenType.CODE_START, TokenType.ENDFOR)) {
if (this.is_at_end()) {
const error = unclosedError('for statement', `for ${iterator_token.value}`, start_token.line, start_token.column, this.source, this.filename);
error.message += getSuggestion(error.message);
throw error;
}
const node = this.parse_content();
if (node) {
body.push(node);
}
}
// Consume endfor
this.consume(TokenType.CODE_START, 'Expected <%');
this.consume(TokenType.ENDFOR, 'Expected endfor');
// Skip optional semicolon
if (this.check(TokenType.JAVASCRIPT)) {
this.advance();
}
const end_token = this.consume(TokenType.TAG_END, 'Expected %>');
return createNode(NodeType.FOR_STATEMENT, {
iterator: iterator_token.value,
body
}, start_token.start, end_token.end, start_token.line, start_token.column, this.create_location(start_token, end_token));
}
// JavaScript reserved words that cannot be used as slot names
static JAVASCRIPT_RESERVED_WORDS = new Set([
// Keywords
'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default',
'delete', 'do', 'else', 'enum', 'export', 'extends', 'false', 'finally',
'for', 'function', 'if', 'import', 'in', 'instanceof', 'let', 'new', 'null',
'return', 'super', 'switch', 'this', 'throw', 'true', 'try', 'typeof',
'var', 'void', 'while', 'with', 'yield',
// Future reserved words
'implements', 'interface', 'package', 'private', 'protected', 'public', 'static', 'await',
// Other problematic words
'arguments', 'eval'
]);
// Parse slot <#name>content</#name> or <#name />
parse_slot() {
const start_token = this.previous(); // SLOT_START
const name_token = this.consume(TokenType.SLOT_NAME, 'Expected slot name');
// Validate slot name against JavaScript reserved words
if (Parser.JAVASCRIPT_RESERVED_WORDS.has(name_token.value.toLowerCase())) {
throw syntaxError(`Slot name "${name_token.value}" is a JavaScript reserved word and cannot be used. Please choose a different name.`, name_token.line, name_token.column, this.source, this.filename);
}
// TODO: Parse attributes for let:prop syntax in future
const attributes = {};
// Check for self-closing slot
if (this.match(TokenType.SLASH)) {
const end_token = this.consume(TokenType.GT, 'Expected >');
return createNode(NodeType.SLOT, {
name: name_token.value,
attributes,
children: [],
selfClosing: true
}, start_token.start, end_token.end, start_token.line, start_token.column, this.create_location(start_token, end_token));
}
// Regular slot with content
this.consume(TokenType.GT, 'Expected >');
const children = [];
// Parse until we find the closing tag
while (!this.check(TokenType.SLOT_END)) {
if (this.is_at_end()) {
const error = unclosedError('slot', name_token.value, name_token.line, name_token.column, this.source, this.filename);
error.message += getSuggestion(error.message);
throw error;
}
const node = this.parse_content();
if (node) {
children.push(node);
}
}
// Consume closing tag
this.consume(TokenType.SLOT_END, 'Expected </#');
const closing_name = this.consume(TokenType.SLOT_NAME, 'Expected slot name');
if (closing_name.value !== name_token.value) {
throw mismatchedTagError(name_token.value, closing_name.value, closing_name.line, closing_name.column, this.source, this.filename);
}
const end_token = this.consume(TokenType.GT, 'Expected >');
return createNode(NodeType.SLOT, {
name: name_token.value,
attributes,
children,
selfClosing: false
}, start_token.start, end_token.end, start_token.line, start_token.column, this.create_location(start_token, end_token));
}
// Token navigation helpers
// Parse HTML tag or component invocation
parse_tag() {
const start_token = this.previous(); // TAG_OPEN
const name_token = this.consume(TokenType.TAG_NAME, 'Expected tag name');
let tag_name = name_token.value;
let original_tag_name = null; // Track original for $redrawable
// Check for forbidden tags
const tag_lower = tag_name.toLowerCase();
if (tag_lower === 'script' || tag_lower === 'style') {
throw syntaxError(`<${tag_name}> tags are not allowed in JQHTML templates. ` +
`Use external files or inline styles via attributes instead.`, name_token.line, name_token.column, this.source, this.filename);
}
// Determine if this is a component (starts with capital letter) or HTML tag
let is_component = tag_name[0] >= 'A' && tag_name[0] <= 'Z';
// Check if this is an HTML5 void element (only for HTML tags, not components)
const is_void_element = !is_component && Parser.VOID_ELEMENTS.has(tag_lower);
// Parse attributes
const attributes = this.parse_attributes();
// Check for $redrawable attribute transformation
// Transform <div $redrawable> to <Redrawable tag="div">
if (attributes['$redrawable'] !== undefined || attributes['data-redrawable'] !== undefined) {
const redrawable_attr = attributes['$redrawable'] !== undefined ? '$redrawable' : 'data-redrawable';
// Remove the $redrawable attribute
delete attributes[redrawable_attr];
// Store original tag name for closing tag matching
original_tag_name = tag_name;
// Add tag="original_tag_name" attribute
attributes['data-tag'] = { quoted: true, value: tag_name };
// Transform tag name to Redrawable (reserved component name)
tag_name = 'Redrawable';
is_component = true; // Now it's a component
}
// Check for explicit self-closing syntax
if (this.match(TokenType.SELF_CLOSING)) {
const end_token = this.previous();
if (is_component) {
return createNode(NodeType.COMPONENT_INVOCATION, {
name: tag_name,
attributes,
children: [],
selfClosing: true
}, start_token.start, end_token.end, start_token.line, start_token.column, this.create_location(start_token, end_token));
}
else {
return createNode(NodeType.HTML_TAG, {
name: tag_name,
attributes,
children: [],
selfClosing: true
}, start_token.start, end_token.end, start_token.line, start_token.column, this.create_location(start_token, end_token));
}
}
// Auto-close void elements even without explicit /> syntax
// This matches standard HTML5 authoring where <input> doesn't need />
if (is_void_element) {
// Skip newlines before >
while (this.match(TokenType.NEWLINE)) {
// Skip newlines
}
const end_token = this.consume(TokenType.GT, 'Expected >');
// Void elements are always HTML tags (not components)
return createNode(NodeType.HTML_TAG, {
name: tag_name,
attributes,
children: [],
selfClosing: true
}, start_token.start, end_token.end, start_token.line, start_token.column, this.create_location(start_token, end_token));
}
// Must be a paired tag - skip newlines before >
while (this.match(TokenType.NEWLINE)) {
// Skip newlines
}
this.consume(TokenType.GT, 'Expected >');
// Parse children
const children = [];
// Keep parsing content until we hit the closing tag
// For $redrawable transforms, accept either original or transformed name
while (!this.check_closing_tag(tag_name) &&
!(original_tag_name && this.check_closing_tag(original_tag_name))) {
if (this.is_at_end()) {
const error = unclosedError(is_component ? 'component' : 'tag', tag_name, start_token.line, start_token.column, this.source, this.filename);
throw error;
}
const child = this.parse_content();
if (child) {
children.push(child);
}
}
// Consume closing tag
this.consume(TokenType.TAG_CLOSE, 'Expected </');
const close_name = this.consume(TokenType.TAG_NAME, 'Expected tag name');
// For $redrawable transforms, accept either original or transformed tag name
const is_valid_closing = close_name.value === tag_name ||
(original_tag_name && close_name.value === original_tag_name);
if (!is_valid_closing) {
throw mismatchedTagError(original_tag_name || tag_name, // Show original name in error
close_name.value, close_name.line, close_name.column, this.source, this.filename);
}
const end_token = this.consume(TokenType.GT, 'Expected >');
if (is_component) {
// Validate mixed content mode for components
this.validate_component_children(children, tag_name, start_token);
return createNode(NodeType.COMPONENT_INVOCATION, {
name: tag_name,
attributes,
children,
selfClosing: false
}, start_token.start, end_token.end, start_token.line, start_token.column, this.create_location(start_token, end_token));
}
else {
return createNode(NodeType.HTML_TAG, {
name: tag_name,
attributes,
children,
selfClosing: false
}, start_token.start, end_token.end, start_token.line, start_token.column, this.create_location(start_token, end_token));
}
}
// Parse attributes from tokens
parse_attributes() {
const attributes = {};
// Skip any leading newlines
while (this.match(TokenType.NEWLINE)) {
// Skip
}
while (this.check(TokenType.ATTR_NAME)) {
const name_token = this.advance();
let name = name_token.value;
let value = true; // Default for boolean attributes
// Check for equals sign and value
if (this.match(TokenType.EQUALS)) {
// Check if this is a compound value with interpolation
if (this.check(TokenType.ATTR_VALUE) ||
this.check(TokenType.EXPRESSION_START) ||
this.check(TokenType.EXPRESSION_UNESCAPED)) {
value = this.parse_attribute_value();
}
}
// Handle special attribute prefixes
if (name.startsWith('$')) {
if (name === '$id') {
// Special case: $id becomes regular id (will be scoped in codegen)
name = 'id';
}
else {
// General case: $property becomes data-property
name = 'data-' + name.substring(1);
// Keep the value object intact to preserve quoted/unquoted distinction
}
}
else if (name.startsWith(':')) {
// Property binding: :prop="value" becomes data-bind-prop
// Preserve whether value was quoted or not for proper code generation
name = 'data-bind-' + name.substring(1);
// Keep the value object intact to preserve quoted/unquoted distinction
}
else if (name.startsWith('@')) {
// Event binding: @click="handler" becomes data-on-click
// Preserve whether value was quoted or not for proper code generation
name = 'data-on-' + name.substring(1);
// Keep the value object intact to preserve quoted/unquoted distinction
}
attributes[name] = value;
// Skip newlines between attributes
while (this.match(TokenType.NEWLINE)) {
// Skip
}
}
return attributes;
}
// Parse potentially compound attribute value
parse_attribute_value() {
const parts = [];
// For simple string values that are quoted in the source, return them with a quoted flag
// This helps the codegen distinguish between $foo="bar" and $foo=bar
const firstToken = this.peek();
const isSimpleValue = this.check(TokenType.ATTR_VALUE) &&
!this.check_ahead(1, TokenType.EXPRESSION_START) &&
!this.check_ahead(1, TokenType.EXPRESSION_UNESCAPED);
// Collect all parts of the attribute value
while (this.check(TokenType.ATTR_VALUE) ||
this.check(TokenType.EXPRESSION_START) ||
this.check(TokenType.EXPRESSION_UNESCAPED)) {
if (this.check(TokenType.ATTR_VALUE)) {
const token = this.advance();
parts.push({ type: 'text', value: token.value, escaped: true });
}
else if (this.check(TokenType.EXPRESSION_START) ||
this.check(TokenType.EXPRESSION_UNESCAPED)) {
const is_escaped = this.peek().type === TokenType.EXPRESSION_START;
this.advance(); // consume <%= or <%!=
const expr_token = this.consume(TokenType.JAVASCRIPT, 'Expected expression');
this.consume(TokenType.TAG_END, 'Expected %>');
parts.push({ type: 'expression', value: expr_token.value, escaped: is_escaped });
}
}
// If it's a single text part, check if it's quoted
if (parts.length === 1 && parts[0].type === 'text') {
const value = parts[0].value;
// Check if the value has quotes (preserved by lexer for quoted strings)
if ((value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))) {
// Return a marker that this was a quoted string
return { quoted: true, value: value.slice(1, -1) };
}
// Check if it's a parenthesized expression: $attr=(expr)
if (value.startsWith('(') && value.endsWith(')')) {
// Return as an expression - remove the parentheses
return { expression: true, value: value.slice(1, -1) };
}
// Check if it's a bare identifier or member expression: $attr=identifier or $attr=this.method
// Valid identifiers can include dots for member access (e.g., this.handleClick, data.user.name)
// Can be prefixed with ! for negation (e.g., !this.canEdit)
// Pattern: optional ! then starts with letter/$/_ then any combo of letters/numbers/$/_ and dots
if (/^!?[a-zA-Z_$][a-zA-Z0-9_$.]*$/.test(value)) {
// Return as an identifier expression
return { identifier: true, value: value };
}
// Otherwise, treat as a literal string value
return value;
}
// Any expression or multiple parts needs interpolation handling
return { interpolated: true, parts };
}
// Check if we're at a closing tag for the given name
check_closing_tag(tag_name) {
if (!this.check(TokenType.TAG_CLOSE)) {
return false;
}
// Look ahead to see if the tag name matches
const next_pos = this.current + 1;
if (next_pos < this.tokens.length &&
this.tokens[next_pos].type === TokenType.TAG_NAME &&
this.tokens[next_pos].value === tag_name) {
return true;
}
return false;
}
match(...types) {
for (const type of types) {
if (this.check(type)) {
this.advance();
return true;
}
}
return false;
}
check(type) {
if (this.is_at_end())
return false;
return this.peek().type === type;
}
check_ahead(offset, type) {
if (this.current + offset >= this.tokens.length) {
return false;
}
return this.tokens[this.current + offset].type === type;
}
check_sequence(...types) {
for (let i = 0; i < types.length; i++) {
if (this.current + i >= this.tokens.length) {
return false;
}
if (this.tokens[this.current + i].type !== types[i]) {
return false;
}
}
return true;
}
advance() {
if (!this.is_at_end())
this.current++;
return this.previous();
}
is_at_end() {
return this.peek().type === TokenType.EOF;
}
peek() {
return this.tokens[this.current];
}
peek_ahead(offset) {
const pos = this.current + offset;
if (pos >= this.tokens.length) {
return this.tokens[this.tokens.length - 1]; // Return EOF token
}
return this.tokens[pos];
}
previous() {
return this.tokens[this.current - 1];
}
current_token() {
return this.tokens[this.current] || this.tokens[this.tokens.length - 1];
}
previous_token() {
return this.tokens[Math.max(0, this.current - 1)];
}
/**
* Create a SourceLocation from start and end tokens
* Propagates loc field if available, falls back to old fields for compatibility
*/
create_location(start, end) {
if (start.loc && end.loc) {
// Use new loc field if available
return {
start: start.loc.start,
end: end.loc.end
};
}
// Fall back to old fields for backward compatibility
return undefined;
}
consume(type, message) {
if (this.check(type))
return this.advance();
const token = this.peek();
// Special case: Detecting template expressions inside HTML tag attributes
if (type === TokenType.GT &&
(token.type === TokenType.EXPRESSION_START || token.type === TokenType.EXPRESSION_UNESCAPED)) {
const error = syntaxError('Template expressions (<% %>) cannot be used as attribute values inside HTML tags', token.line, token.column, this.source, this.filename);
// Add helpful remediation examples
error.message += '\n\n' +
' Use template expressions INSIDE attribute values instead:\n' +
' ✅ <tag style="<%= expression %>">\n' +
' ✅ <tag class="<%= condition ? \'active\' : \'\' %>">\n\n' +
' Or use conditional logic before the tag:\n' +
' ✅ <% let attrs = expression ? \'value\' : \'\'; %>\n' +
' <tag attr="<%= attrs %>">\n\n' +
' Or set attributes in on_ready() using jQuery:\n' +
' ✅ <tag $id="my_element">\n' +
' on_ready() {\n' +
' if (this.args.required) this.$id(\'my_element\').attr(\'required\', true);\n' +
' }';
throw error;
}
const error = syntaxError(`${message}. Got ${token.type} instead`, token.line, token.column, this.source, this.filename);
throw error;
}
// Validate component children to prevent mixed content mode
validate_component_children(children, componentName, startToken) {
let hasSlots = false;
let hasNonSlotContent = false;
for (const child of children) {
if (child.type === NodeType.SLOT) {
hasSlots = true;
}
else if (child.type === NodeType.TEXT) {
// Check if it's non-whitespace text
const textContent = child.content;
if (textContent.trim() !== '') {
hasNonSlotContent = true;
}
}
else {
// Any other node type (expressions, tags, etc.) is non-slot content
hasNonSlotContent = true;
}
}
// If component has both slots and non-slot content, throw error
if (hasSlots && hasNonSlotContent) {
throw syntaxError(`Mixed content not allowed: when using slots, all content must be inside <#slotname> tags`, startToken.line, startToken.column, this.source, this.filename);
}
}
/**
* Compile method for simplified API
* Parses the template and returns component metadata and render function
*/
compile() {
// Parse to get AST
const ast = this.parse();
// Generate code with sourcemap
const generator = new CodeGenerator();
const result = generator.generateWithSourceMap(ast, this.filename || 'template.jqhtml', this.source || '');
// Extract the single component (should only be one per file)
const componentEntries = Array.from(result.components.entries());
if (componentEntries.length === 0) {
throw new Error('No component definition found in template');
}
if (componentEntries.length > 1) {
const names = componentEntries.map(([name]) => name).join(', ');
throw new Error(`Multiple component definitions found: ${names}. Only one component per file is allowed.`);
}
// Extract component information
const [name, componentDef] = componentEntries[0];
return {
name: name,
tagName: componentDef.tagName || 'div',
defaultAttributes: componentDef.defaultAttributes || {},
renderFunction: componentDef.render_function
};
}
}
//# sourceMappingURL=parser.js.map

1
node_modules/@jqhtml/parser/dist/parser.js.map generated vendored Executable file

File diff suppressed because one or more lines are too long

6
node_modules/@jqhtml/parser/dist/runtime.d.ts generated vendored Executable file
View File

@@ -0,0 +1,6 @@
export declare function process_instructions(instructions: any[], component: any): JQuery;
export declare function html(str: any): string;
export declare function register_template(name: string, render_fn: Function): void;
export declare function get_template(name: string): Function | undefined;
export declare function create_templated_component(ComponentClass: any, template_name: string, args?: Record<string, any>, element?: JQuery): any;
//# sourceMappingURL=runtime.d.ts.map

1
node_modules/@jqhtml/parser/dist/runtime.d.ts.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAMA,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,GAAG,EAAE,EAAE,SAAS,EAAE,GAAG,GAAG,MAAM,CA6EhF;AAGD,wBAAgB,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAMrC;AAMD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,QAElE;AAGD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAE/D;AAGD,wBAAgB,0BAA0B,CACxC,cAAc,EAAE,GAAG,EACnB,aAAa,EAAE,MAAM,EACrB,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,EAC9B,OAAO,CAAC,EAAE,MAAM,GACf,GAAG,CAsBL"}

106
node_modules/@jqhtml/parser/dist/runtime.js generated vendored Executable file
View File

@@ -0,0 +1,106 @@
// JQHTML v2 Runtime - Processes instruction arrays from compiled templates
// Minimal implementation focused on MVP functionality
/// <reference path="./types.d.ts" />
// Process instruction array into DOM elements
export function process_instructions(instructions, component) {
const fragment = document.createDocumentFragment();
const stack = [];
let current = fragment;
for (const instruction of instructions) {
// Plain text
if (typeof instruction === 'string') {
const text = document.createTextNode(instruction);
current.appendChild(text);
continue;
}
// Tag instruction: {tag: [name, attrs, selfClosing]}
if (instruction.tag) {
const [tag_name, attrs, self_closing] = instruction.tag;
const element = document.createElement(tag_name);
// Set attributes
for (const [key, value] of Object.entries(attrs || {})) {
element.setAttribute(key, String(value));
}
current.appendChild(element);
// Push to stack if not self-closing
if (!self_closing) {
stack.push(current);
current = element;
}
continue;
}
// Component instruction: {comp: [name, props]}
if (instruction.comp) {
const [comp_name, props] = instruction.comp;
// Create placeholder div for component
const placeholder = document.createElement('div');
placeholder.setAttribute('data-component', comp_name);
placeholder.setAttribute('data-props', JSON.stringify(props || {}));
placeholder.setAttribute('data-state', 'uninitialized');
placeholder.className = 'jqhtml-component';
current.appendChild(placeholder);
// TODO: In future, instantiate component here
continue;
}
// Slot instruction: {slot: [name, props, renderFn]}
if (instruction.slot) {
const [slot_name, props, render_fn] = instruction.slot;
// Execute slot render function if provided
if (render_fn && typeof render_fn === 'function') {
const slot_context = { ...props };
const [slot_output] = render_fn.call(component, slot_context);
// Process slot output recursively
const slot_dom = process_instructions(slot_output, component);
slot_dom.each(function () {
current.appendChild(this);
});
}
continue;
}
// Closing tag (when we see a string like "</div>")
if (typeof instruction === 'string' && instruction.match(/^<\//)) {
if (stack.length > 0) {
current = stack.pop();
}
}
}
return $(fragment.childNodes);
}
// HTML escape function required by generated code
export function html(str) {
if (str == null)
return '';
const div = document.createElement('div');
div.textContent = String(str);
return div.innerHTML;
}
// Component template registry
const template_registry = new Map();
// Register a compiled template
export function register_template(name, render_fn) {
template_registry.set(name, render_fn);
}
// Get a compiled template
export function get_template(name) {
return template_registry.get(name);
}
// Create component with template
export function create_templated_component(ComponentClass, template_name, args = {}, element) {
const template = get_template(template_name);
if (!template) {
throw new Error(`Template not found: ${template_name}`);
}
// Create component instance - element first, then args
const component = new ComponentClass(element, args);
// Override on_render to use template
component.on_render = async function () {
// Call template render function
const [instructions] = template.call(this, ComponentClass, this.data, this.args, {});
// Process instructions into DOM
const rendered = process_instructions(instructions, this);
// Clear and append
this.$.empty().append(rendered);
};
return component;
}
//# sourceMappingURL=runtime.js.map

1
node_modules/@jqhtml/parser/dist/runtime.js.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}

61
node_modules/@jqhtml/parser/package.json generated vendored Executable file
View File

@@ -0,0 +1,61 @@
{
"name": "@jqhtml/parser",
"version": "2.2.137",
"description": "JQHTML template parser - converts templates to JavaScript",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"bin": {
"jqhtml-compile": "./bin/jqhtml-compile"
},
"scripts": {
"build": "tsc && node ../../scripts/inject-parser-version.js",
"build:no-inject": "tsc",
"test": "jest",
"clean": "rm -rf dist",
"baseline:generate": "node test-regression/generate-baseline.js",
"test:regression": "node test-regression/compare-outputs.js",
"test:regression:verbose": "node test-regression/compare-outputs.js --verbose",
"validate:sourcemap": "node test-regression/validate-sourcemaps.js"
},
"browser": false,
"engines": {
"node": ">=14.0.0"
},
"publishConfig": {
"access": "public",
"registry": "https://privatenpm.hanson.xyz/"
},
"files": [
"dist",
"README.md",
"LLM_REFERENCE.md",
"LICENSE"
],
"keywords": [
"jqhtml",
"parser",
"template",
"compiler",
"jquery"
],
"author": "JQHTML Team",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/jqhtml/jqhtml.git",
"directory": "packages/parser"
},
"dependencies": {
"@types/jest": "^29.5.11",
"@types/jquery": "^3.5.32",
"@types/node": "^20.10.5",
"jest": "^29.7.0",
"source-map": "^0.7.4",
"typescript": "^5.3.3"
},
"devDependencies": {
"chalk": "^5.6.2",
"diff": "^8.0.2"
}
}