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/core/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.

369
node_modules/@jqhtml/core/LLM_REFERENCE.md generated vendored Executable file
View File

@@ -0,0 +1,369 @@
# JQHTML Template & Component System - LLM Reference
## Component-Template Association
- Template files (.jqhtml) compile to ES6 classes extending Component base class
- Template name matches component class name: `UserCard.jqhtml``class UserCard extends Component`
- Components are jQuery objects: `this.$` is the component's root jQuery element
- No virtual DOM, no reconciliation, direct DOM manipulation only
## Template Syntax
### Basic Structure
```jqhtml
<Define:ComponentName tag="tagname">
<!-- template content -->
</Define:ComponentName>
```
- `tag` attribute optional, defaults to "div"
- Component instantiation: `<ComponentName />` or `<ComponentName tag="span" />`
- Tag can be overridden at invocation time
- Self-closing for no children, paired tags for slots/children
### Data Binding & Expressions
- `<%= expression %>` - Escaped output (HTML entities encoded)
- `<%! expression %>` - Raw output (no escaping)
- `<% statement; %>` - JavaScript statements (no output)
- Access component data via `this.data.property`
- Access component methods via `this.methodName()`
### Attribute System
- `$id="name"``id="name:_cid"` (component-scoped ID)
- `$attr="value"``data-attr="value"` (data attributes)
- `@click="handler"` → jQuery event binding to component method
- `@submit.prevent="handler"` → Event with preventDefault
- Regular attributes pass through unchanged
### Control Flow
Two equivalent syntaxes supported:
**Colon Syntax:**
```jqhtml
<% for (let item of items): %>
<div><%= item %></div>
<% endfor; %>
<% if (condition): %>
<div>True branch</div>
<% else: %>
<div>False branch</div>
<% endif; %>
```
**Brace Syntax:**
```jqhtml
<% for (let item of items) { %>
<div><%= item %></div>
<% } %>
```
### Component Content & innerHTML (Primary Pattern)
**Most components use the simple `content()` function** to output innerHTML:
```jqhtml
<!-- Define a component that outputs innerHTML -->
<Define:Card>
<div class="card">
<div class="card-body">
<%= content() %> <!-- Outputs whatever innerHTML was passed -->
</div>
</div>
</Define:Card>
<!-- Usage - just pass innerHTML like regular HTML -->
<Card>
<h3>Card Title</h3>
<p>Card content goes here</p>
<button>Action</button>
</Card>
```
This is the **recommended pattern for 95% of components**. It's simple, intuitive, and works like regular HTML.
### Slots (Advanced Feature)
**Slots are only needed when you have multiple distinct content areas:**
```jqhtml
<Define:ComplexCard>
<div class="card">
<div class="card-header">
<%= content('header') %>
</div>
<div class="body">
<%= content() %>
</div>
<div class="card-footer">
<%= content('footer') %>
</div>
</div>
</Define:ComplexCard>
<!-- Usage with slots (advanced): -->
<ComplexCard>
<#header>Title</#header>
Main content
<#footer>Actions</#footer>
</ComplexCard>
```
**Important:** You cannot mix modes - if using slots, ALL content must be in slots.
## Component Lifecycle
### Execution Order
1. **constructor** - Synchronous, sets up jQuery wrapper
2. **render** - Creates DOM structure (top-down traversal)
3. **create** - Quick initialization (bottom-up traversal)
4. **load** - Async data fetching (parallel execution)
5. **ready** - Final setup, DOM complete (bottom-up traversal)
### Phase Characteristics
- **render**: Executes parent before children, can access `this.$` and `this.data`
- **create**: Executes children before parents, component visible but may lack data
- **load**: All components at same level run in parallel, NO DOM manipulation allowed
- **ready**: Executes children before parents, all data loaded, safe for DOM queries
### Lifecycle Methods
```javascript
class MyComponent extends Component {
async on_render() {
// Called during render phase, after template DOM created
// Parent's on_render completes before children render
}
async on_create() {
// Bottom-up, children complete before parent
// Used for quick setup, event binding
}
async on_load() {
// Parallel execution, data fetching only
// MUST NOT touch DOM or component will break
this.data.users = await fetch('/api/users').then(r => r.json());
}
async on_ready() {
// Bottom-up, all components fully initialized
// Safe to query children, cross-component communication
}
}
```
## Args vs Data: Critical Distinction
### this.args - Input Parameters
Input parameters passed to the component during invocation. These come from:
- `$` attributes: `$user=this.user` (expression) or `$title="literal"` (string)
- `data-*` attributes: `data-role="admin"`
- Regular attributes: `name="John"` (becomes `data-name` in args)
```javascript
// Parent template
<UserCard
$user=this.user // args.user = parent's user object
$title="User Profile" // args.title = "User Profile" (literal string)
data-role="admin" // args.role = "admin"
email="john@example" // args.email = "john@example"
/>
// In UserCard component
on_create() {
console.log(this.args.user); // {name: "Alice", id: 123}
console.log(this.args.title); // "User Profile"
console.log(this.args.role); // "admin"
console.log(this.args.email); // "john@example"
}
```
### this.data - Async Loaded Data
Data loaded asynchronously in the `on_load()` lifecycle method:
```javascript
class UserCard extends Jqhtml_Component {
async on_load() {
// Load data from API - this populates this.data
this.data.posts = await fetch(`/api/users/${this.args.user.id}/posts`)
.then(r => r.json());
// this.args = input parameters (unchanged)
// this.data = fetched data (just loaded)
}
on_ready() {
// Both args and data are now available
console.log('User:', this.args.user); // Input parameter
console.log('Posts:', this.data.posts); // Loaded data
}
}
```
### Key Differences
| Property | `this.args` | `this.data` |
|----------|------------|------------|
| **Source** | Parent component attributes | `on_load()` method |
| **When Set** | Component construction | Load lifecycle phase |
| **Purpose** | Configuration/parameters | Dynamic/async data |
| **Reactivity** | No (static after construction) | Yes (changes can trigger re-render) |
| **Example** | User ID, config options | API responses, computed values |
## Component API
### Core Properties
- `this.$` - jQuery object for component root element
- `this.args` - Input parameters from component invocation (attributes passed from parent)
- `this.data` - Data loaded asynchronously in `on_load()` method
- `this._cid` - Unique component instance ID
- `this._ready_state` - Lifecycle phase (0=created, 1=rendering, 2=creating, 3=loading, 4=ready)
### DOM Access Methods
- `this.$id('name')` - Get element by scoped ID (returns jQuery object)
- `this.$child('name')` - Get child component by name
- `this.$children()` - Get all direct child components
- `this.$parent()` - Get parent component
- `this.$find('.selector')` - Query within component scope
### Data Management
```javascript
// Setting data triggers re-render after current lifecycle phase
this.data.property = value; // Triggers render after load phase
this.set_data({ multiple: values }); // Batch update
// Prevent re-render during load phase
async on_load() {
this.data.items = await fetchItems(); // Won't trigger render
this.data.cache = processItems(this.data.items); // Still won't render
// Render happens once after all load phases complete
}
```
### Component Registration
```javascript
// Global registration (optional)
jqhtml.register_component('UserCard', UserCardClass);
// Template registration (for dynamic components)
jqhtml.register_template('UserCard', {
as: 'article',
render: compiledRenderFunction
});
// Dynamic instantiation
const ComponentClass = jqhtml.get_component_class('UserCard');
const instance = new ComponentClass({ data: initialData });
$('#container').append(instance.$);
```
### jQuery Method Override
```javascript
class CustomInput extends Component {
val(value) {
if (arguments.length === 0) {
// Getter - return processed value
return this.parse_value(this.$id('input').val());
} else {
// Setter - validate and set
if (this.validate(value)) {
this.$id('input').val(this.format_value(value));
this.data.value = value;
}
return this.$; // Maintain jQuery chaining
}
}
}
// Usage: $('#my-input').val() calls component's val method
```
## Instruction Format (Compiled Template Output)
Templates compile to instruction arrays:
```javascript
[
{tag: ['div', {class: 'container', $id: 'root'}, false]},
{text: 'Static content'},
{expr: function() { return this.data.title; }},
{comp: ['ChildComponent', {props: 'values'}, [
{slot: ['header', [...instructions]]}
]]},
{ctrl: ['if', function() { return this.data.show; }, [...instructions]]}
]
```
## Component Instantiation Patterns
### jQuery Plugin
```javascript
$('#target').component(ComponentClass, { data: initialData });
```
### Direct Instantiation
```javascript
const component = new ComponentClass({ data: initialData });
$('#target').append(component.$);
await component.wait_ready(); // Wait for ready state
```
### Within Templates
```jqhtml
<UserCard $data="this.data.user" @click="handleUserClick" />
```
## Re-render Behavior
- Data changes during `load` phase are batched, single render after all loads complete
- Data changes during `ready` phase trigger immediate re-render
- Re-render preserves component instance, only updates DOM
- Child components maintain state through re-render unless key changes
- `should_rerender()` method can prevent unnecessary renders
## Event Handling
```jqhtml
<!-- Method binding -->
<button @click="handleClick">Click</button>
<!-- Inline expression -->
<button @click="this.data.count++">Increment</button>
<!-- With event modifiers -->
<form @submit.prevent="handleSubmit">
```
```javascript
class Component {
handleClick(event) {
// jQuery event object
// 'this' is component instance
event.preventDefault();
this.data.clicked = true;
}
}
```
## Component Communication
```javascript
// Parent to child - via props
<ChildComponent $data="this.data.childData" />
// Child to parent - via events
this.$.trigger('custom-event', [data]);
// Parent listening
this.$child('name').on('custom-event', (e, data) => {});
// Cross-component - via registry
const other = jqhtml.get_component_instance('id');
```
## Performance Characteristics
- Template compilation happens build-time, zero runtime parsing
- jQuery operations are synchronous and immediate
- No virtual DOM overhead, direct DOM manipulation
- Component creation is fast, most cost in load phase
- Re-renders only update changed portions via jQuery
## Critical Invariants
1. Never modify DOM during `load` phase - causes race conditions
2. Always return jQuery object from overridden jQuery methods for chaining
3. Component `this.$` is always valid after constructor
4. Data modifications during load are batched to prevent render thrashing
5. Bottom-up phases (create, ready) guarantee children initialize first
6. Parallel load execution means no component dependencies in load phase
7. Template expressions have access to full component context via `this`

565
node_modules/@jqhtml/core/README.md generated vendored Executable file
View File

@@ -0,0 +1,565 @@
# @jqhtml/core
Core runtime library for JQHTML v2 - jQuery-first component framework.
## Installation
```bash
npm install @jqhtml/core jquery
```
## Module Structure
The core package exports two separate entry points:
### Main Runtime (Production)
```javascript
import { Component, jqhtml } from '@jqhtml/core';
```
### Debug Utilities (Development Only)
```javascript
import { showDebugOverlay, hideDebugOverlay } from '@jqhtml/core/debug';
```
The debug module is kept separate to avoid including debug code in production bundles. It provides:
- Visual debug overlay with performance profiling
- Component flash visualization
- Lifecycle logging
- Slow render detection
## Overview
The core package provides:
- Component base class with 5-stage lifecycle
- Lifecycle manager for phase coordination
- Component registry for dynamic instantiation
- Instruction processor for template rendering
- jQuery plugin integration
- Template rendering with data bindings
## Quick Start
### Basic Component
```javascript
import { Component } from '@jqhtml/core';
class UserCard extends Component {
async init() {
// Quick setup, hide elements
this.$.addClass('loading');
}
async load() {
// Fetch data (runs in parallel with siblings)
this.data.user = await fetch('/api/user').then(r => r.json());
}
async render() {
// Create DOM structure
this.$.html(`
<div class="card">
<h2>${this.data.user.name}</h2>
<p>${this.data.user.email}</p>
</div>
`);
}
async ready() {
// Component fully initialized
this.$.removeClass('loading');
}
}
```
### Using jQuery Plugin
```javascript
// Create component on existing element
$('#user-container').component(UserCard, { userId: 123 });
// Get component instance
const card = $('#user-container').component();
```
## Component Lifecycle
Components follow a multi-stage lifecycle with automatic re-rendering:
1. **constructor** - Instance creation, jQuery element setup (`this.data` is `{}`)
2. **render** - Create initial DOM structure (top-down, atomic/synchronous operation)
3. **create** - Quick setup after DOM creation (bottom-up, siblings parallel)
4. **load** - Fetch data (bottom-up, fully parallel, **ABSOLUTELY NO DOM MODIFICATIONS**)
5. **render** (automatic) - Re-render if data changed during load (empties DOM first)
6. **create** (automatic) - Re-setup if component was re-rendered
7. **ready** - Fully initialized (bottom-up, siblings parallel)
8. **destroy** - Cleanup when removed
### Critical: NO DOM Modifications in load()
**The requirement that `on_load()` must not modify the DOM is ABSOLUTE.** This is not a guideline but a strict architectural requirement. Violating this will cause rendering issues and race conditions.
**Data Initialization Timeline:**
- Before `load()`: `this.data = {}` (empty object)
- After `load()`: `this.data` contains fetched data (remains `{}` if no data loaded)
### Correct Patterns for Loading States
If you need to show different DOM states before and after loading, use these patterns:
**Pattern 1: Conditional Rendering in Template**
```jqhtml
<Define:UserProfile>
<div class="user-profile">
<% if (Object.keys(this.data).length === 0): %>
<!-- Show loading state (no data loaded yet) -->
<div class="loading">
<div class="spinner"></div>
<p>Loading user profile...</p>
</div>
<% else: %>
<!-- Show loaded content -->
<h2><%= this.data.user?.name || 'Unknown User' %></h2>
<p><%= this.data.user?.bio || 'No bio available' %></p>
<% endif; %>
</div>
</Define:UserProfile>
```
**Pattern 2: DOM Changes in create() and ready()**
```javascript
class DataComponent extends Component {
async on_create() {
// Set loading state in the DOM during create phase
this.$id('status').addClass('loading').text('Loading...');
}
async on_load() {
// ONLY fetch data - NO DOM modifications here!
this.data.user = await fetch('/api/user').then(r => r.json());
}
async on_ready() {
// Update DOM after data is loaded
this.$id('status').removeClass('loading').text('Loaded');
this.$id('username').text(this.data.user.name);
}
}
```
**NEVER do this:**
```javascript
class BadComponent extends Component {
async on_load() {
// ❌ WRONG - DOM modification in load()
this.$id('status').text('Loading...'); // VIOLATION!
this.data.user = await fetch('/api/user').then(r => r.json());
// ❌ WRONG - More DOM modification
this.$id('status').text('Loaded'); // VIOLATION!
}
}
```
**Important:** The `render()` method is atomic and essentially synchronous - it empties the DOM and rebuilds it in a single operation. While marked `async` for technical reasons, it completes immediately without yielding control.
### Phase Batching
All components complete each phase before moving to the next:
- Parents render before children
- Children create/load/ready before parents
- Siblings execute in parallel where safe
### Automatic Re-rendering
After the `load` phase, if `this.data` has changed, the component automatically:
1. Calls `render()` again (which empties the DOM with `$.empty()` first)
2. Calls `create()` again to re-setup the new DOM
3. Then proceeds to `ready()`
This ensures components can fetch data and re-render with that data seamlessly.
### Controlling Re-rendering
By default, components automatically re-render if `this.data` changes during `load()`:
```javascript
class DataComponent extends Component {
async on_load() {
// If this modifies this.data, component will re-render
this.data.user = await fetch('/api/user').then(r => r.json());
}
// Override to customize re-render logic
should_rerender() {
// Default: returns true if JSON.stringify(this.data) changed
// Override for custom logic:
return this.data.user && !this.data.cached;
}
}
```
### Manual Re-rendering
You can manually re-render a component at any time:
```javascript
class InteractiveComponent extends Component {
async handleUpdate(newData) {
// Update data
this.data = { ...this.data, ...newData };
// Manually re-render (atomic operation)
await this.render();
// Optionally re-initialize (call create/ready as needed)
await this.on_create(); // Re-setup event handlers
await this.on_ready(); // Re-initialize component state
}
// Lifecycle manipulation methods
redraw() {
// Synchronously re-renders with current data
}
async reload_data() {
// Re-fetches data via on_load(), then redraws
}
async reinitialize() {
// Full lifecycle reset from stage 0
}
destroy() {
// Cleanup component and children
}
}
```
**Note:** The re-render process:
1. Empties the component's DOM with `this.$.empty()`
2. Calls `render()` to rebuild the DOM atomically
3. For automatic re-renders, calls `create()` again for the new DOM
4. Finally calls `ready()`
5. For manual re-renders, you control which lifecycle methods to call
## Scoped IDs
Components use `_cid` for scoped element selection:
```javascript
class TabsComponent extends Component {
async render() {
this.$.html(`
<button id="tab1:${this._cid}">Tab 1</button>
<button id="tab2:${this._cid}">Tab 2</button>
<div id="content:${this._cid}"></div>
`);
}
selectTab(tabId) {
// Use $id() for scoped selection
this.$id('tab1').removeClass('active');
this.$id('tab2').removeClass('active');
this.$id(tabId).addClass('active');
}
}
```
## Template Integration
The core runtime processes templates compiled by `@jqhtml/parser`:
```javascript
import { render_template, with_template } from '@jqhtml/core';
// Template function (usually generated by parser)
const template = function(data, args, content) {
const _output = [];
_output.push({tag: ["h1", {"data-bind-text": "title"}, false]});
_output.push({tag: ["h1", {}, true]});
return [_output, this];
};
// Component with template
class Article extends Component {
async on_render() {
await render_template(this, template);
}
}
// Or use mixin
const Article = with_template(template)(Component);
```
## Attribute Rules
JQHTML has specific rules for attribute quoting and value passing:
- **@ Event attributes**: MUST be unquoted (pass function references)
Example: `@click=this.handleClick`
- **$ Data attributes**: Can be quoted OR unquoted (flexible)
Example: `$id="my-id"` or `$data=this.complexObject`
- **Regular HTML attributes**: MUST be quoted (strings only)
Example: `class="container <%= this.args.theme %>"`
See [ATTRIBUTE_RULES.md](../../docs/ATTRIBUTE_RULES.md) for comprehensive documentation.
## Data Bindings
Templates support reactive data bindings:
- `:text="expression"` - Update text content
- `:value="expression"` - Form input values
- `:class="{active: isActive}"` - Dynamic classes
- `:style="{color: textColor}"` - Dynamic styles
- `@click=handler` - Event handlers (unquoted)
## Browser Bundle
For browser usage without a build tool:
```html
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="jqhtml-bundle.js"></script>
<script>
const { Component } = JQHTML;
class MyComponent extends Component {
// ...
}
</script>
```
## Lifecycle Event Callbacks
JQHTML provides a `.on()` method for registering callbacks that fire after lifecycle events complete. This is useful for external code that needs to know when a component reaches a certain state.
### Supported Events
- `'render'` - Fires after the render phase completes
- `'create'` - Fires after the create phase completes
- `'load'` - Fires after the load phase completes
- `'ready'` - Fires after the ready phase completes (component fully initialized)
### Usage
```javascript
// Get component instance and register callback
const component = $('#my-component').component();
component.on('ready', (comp) => {
console.log('Component is ready!', comp);
// Access component data, DOM, etc.
});
// If the event already occurred, callback fires immediately
// AND still registers for future occurrences (e.g., on re-render)
```
### Behavior
1. **Immediate Execution**: If the lifecycle event has already occurred when you register the callback, it fires immediately
2. **Future Events**: The callback also registers for future occurrences of the same event (useful for re-renders)
3. **Multiple Callbacks**: You can register multiple callbacks for the same event
4. **Error Safety**: Errors in callbacks are caught and logged without breaking the component
5. **Validation**: Only lifecycle events are allowed; other event names trigger a console error
### Example: Wait for Component to be Ready
```javascript
// Register callback before or after component initialization
$('#user-profile').component().on('ready', (component) => {
// Component is fully initialized, data is loaded
console.log('User data:', component.data.user);
// Safe to access all DOM elements
component.$id('email').addClass('verified');
});
```
### Example: Track Multiple Lifecycle Events
```javascript
const component = $('#dashboard').component();
component
.on('render', () => console.log('Dashboard rendered'))
.on('create', () => console.log('Dashboard created'))
.on('load', () => console.log('Dashboard data loaded'))
.on('ready', () => console.log('Dashboard ready'));
```
### Example: Invalid Event (Error)
```javascript
// This will log an error to console
$('#my-component').component().on('click', callback);
// Error: Component.on() only supports lifecycle events: render, create, load, ready
```
### Note on Re-renders
When a component re-renders (manually or automatically after `load()`), lifecycle events fire again:
```javascript
const component = $('#widget').component();
component.on('render', () => {
console.log('Widget rendered');
// Fires on initial render AND every re-render
});
// Later, trigger re-render
await component.render(); // "Widget rendered" logs again
```
## API Reference
### Component Class
- `constructor(element, args)` - Create component instance
- `async render()` - Create DOM structure
- `async init()` - Quick setup phase
- `async load()` - Data fetching phase
- `async ready()` - Final initialization
- `destroy()` - Cleanup
- `should_rerender()` - Control re-rendering after load
- `emit(event, data)` - Emit jQuery events
- `on(event, callback)` - Register lifecycle event callback
- `$id(localId)` - Get scoped jQuery element
- `id(localId)` - Get scoped component instance
- `parent()` - Get parent component
- `children()` - Get direct child components
- `find(selector)` - Find descendant components
### jQuery Plugin
- `$(el).component()` - Get component instance
- `$(el).component(Class, args)` - Create component
- `$.jqhtml.register(name, Class)` - Register component
- `$.jqhtml.create(name, args)` - Create by name
### Template Functions
- `render_template(component, template)` - Render template
- `with_template(template)` - Component mixin
- `process_instructions(instructions, target, context)` - Process instruction array
## Component and Template Registration Flexibility
JQHTML provides complete flexibility in how you define components - you can use JavaScript classes, `.jqhtml` template files, both, or neither. The framework automatically handles all combinations:
### All Possible Combinations
1. **Both JS Class and .jqhtml Template** (Standard approach)
```javascript
// UserCard.js
class UserCard extends Jqhtml_Component {
async load() {
this.data.user = await fetch('/api/user').then(r => r.json());
}
}
jqhtml.register_component('UserCard', UserCard);
```
```html
<!-- UserCard.jqhtml -->
<Define:UserCard>
<div class="card">
<h2><%= this.data.user.name %></h2>
</div>
</Define:UserCard>
```
**Result**: Uses your custom class logic + your custom template
2. **.jqhtml Template Only** (No JS class needed)
```html
<!-- SimpleWidget.jqhtml -->
<Define:SimpleWidget>
<div class="widget">
Hello, <%= this.args.name %>!
</div>
</Define:SimpleWidget>
```
**Result**: Uses default `Jqhtml_Component` class + your template. Perfect for presentational components that don't need custom logic.
3. **JS Class Only** (No .jqhtml template)
```javascript
class DynamicComponent extends Jqhtml_Component {
async render() {
this.$.html(`<div>Rendered at ${Date.now()}</div>`);
}
}
jqhtml.register_component('DynamicComponent', DynamicComponent);
```
**Result**: Uses your class + default passthrough template. Useful when you want full programmatic control.
4. **Neither Class nor Template** (Passthrough)
```html
<!-- In your JQHTML file -->
<FooBar>
<p>This content just passes through!</p>
</FooBar>
```
**Result**: Uses default `Jqhtml_Component` class + default passthrough template. Acts as a simple wrapper that renders its inner content.
### How It Works
When you use `<ComponentName>`:
- **JS Class Resolution**: Looks for registered class, falls back to `Jqhtml_Component`
- **Template Resolution**: Looks for registered template by class, then by name, then uses default passthrough
- **Default Passthrough Template**: Simply renders the component's inner HTML as-is
This means you can start simple and progressively add complexity:
```html
<!-- Start with just markup -->
<UserBadge>Basic HTML content</UserBadge>
<!-- Later, add a .jqhtml template for consistent styling -->
<Define:UserBadge>
<div class="badge"><%= this.args.username %></div>
</Define:UserBadge>
<!-- Finally, add JS class for complex behavior -->
class UserBadge extends Jqhtml_Component {
async load() { /* fetch user data */ }
}
```
## Laravel Integration
This package includes a Laravel bridge for error handling and source map support. After installing via npm, you can load it directly from `node_modules` in your Laravel application:
```php
// In app/Providers/AppServiceProvider.php
public function register()
{
$jqhtmlBridge = base_path('node_modules/@jqhtml/core/laravel-bridge/autoload.php');
if (file_exists($jqhtmlBridge)) {
require_once $jqhtmlBridge;
}
}
```
See [laravel-bridge/LARAVEL_INTEGRATION.md](./laravel-bridge/LARAVEL_INTEGRATION.md) for complete integration instructions.
## Development
```bash
# Install dependencies
npm install
# Build TypeScript
npm run build
# Run tests
npm test
# Watch mode
npm run watch
```
## License
MIT

62
node_modules/@jqhtml/core/dist/component-registry.d.ts generated vendored Executable file
View File

@@ -0,0 +1,62 @@
/**
* JQHTML v2 Component Registry
*
* Global registry for component classes and templates
* Enables dynamic component instantiation and template association
*/
import { Jqhtml_Component } from './component.js';
export type TemplateFunction = (this: Jqhtml_Component, data: Record<string, any>, args: Record<string, any>, content: any) => [any[], Jqhtml_Component];
export interface TemplateDefinition {
name: string;
tag: string;
defaultAttributes?: Record<string, any>;
render: TemplateFunction;
}
export type ComponentConstructor = new (args: Record<string, any>, element?: any) => Jqhtml_Component;
/**
* Register a component class with optional template
*/
export declare function register_component(name: string, component_class: ComponentConstructor, template?: TemplateDefinition): void;
export declare function register_component(component_class: ComponentConstructor): void;
/**
* Get a component class by name
*/
export declare function get_component_class(name: string): ComponentConstructor | undefined;
/**
* Register a template - name is extracted from template.name property
* Returns true if registered, false if duplicate
*/
export declare function register_template(template_def: TemplateDefinition): boolean;
/**
* Get template for a component by name
*/
export declare function get_template(name: string): TemplateDefinition;
/**
* Get template for a component class - walks up inheritance chain
*/
export declare function get_template_by_class(component_class: ComponentConstructor): TemplateDefinition;
/**
* Create a component instance by name
* If no component class is registered, uses the default Component class
*/
export declare function create_component(name: string, element?: any, args?: Record<string, any>): Jqhtml_Component;
/**
* Check if a component is registered
*/
export declare function has_component(name: string): boolean;
/**
* Get all registered component names
*/
export declare function get_component_names(): string[];
/**
* Get all registered template names
*/
export declare function get_registered_templates(): string[];
/**
* List all registered components with their template status
*/
export declare function list_components(): Record<string, {
has_class: boolean;
has_template: boolean;
}>;
//# sourceMappingURL=component-registry.d.ts.map

1
node_modules/@jqhtml/core/dist/component-registry.d.ts.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"component-registry.d.ts","sourceRoot":"","sources":["../src/component-registry.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGlD,MAAM,MAAM,gBAAgB,GAAG,CAC7B,IAAI,EAAE,gBAAgB,EACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,OAAO,EAAE,GAAG,KACT,CAAC,GAAG,EAAE,EAAE,gBAAgB,CAAC,CAAC;AAG/B,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACxC,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAGD,MAAM,MAAM,oBAAoB,GAAG,KAAK,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,GAAG,KAAK,gBAAgB,CAAC;AAsCtG;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,eAAe,EAAE,oBAAoB,EACrC,QAAQ,CAAC,EAAE,kBAAkB,GAC5B,IAAI,CAAC;AACR,wBAAgB,kBAAkB,CAAC,eAAe,EAAE,oBAAoB,GAAG,IAAI,CAAC;AA4ChF;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,SAAS,CAElF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,kBAAkB,GAAG,OAAO,CAoC3E;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,CA0B7D;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,eAAe,EAAE,oBAAoB,GAAG,kBAAkB,CAwB/F;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,GAAG,EACb,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,GAC7B,gBAAgB,CAGlB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAE9C;AAED;;GAEG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,EAAE,CAEnD;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,OAAO,CAAA;CAAE,CAAC,CAsB/F"}

148
node_modules/@jqhtml/core/dist/component.d.ts generated vendored Executable file
View File

@@ -0,0 +1,148 @@
/**
* JQHTML v2 Component Base Class
*
* Core component implementation following v2 specification:
* - 5-stage lifecycle coordinated by LifecycleManager
* - Direct jQuery manipulation (no virtual DOM)
* - Scoped IDs using _cid pattern
* - Event emission and CSS class hierarchy
*/
declare global {
interface Window {
JQHTML_DEBUG?: {
log: (componentName: string, phase: string, message: string, data?: any) => void;
updateTree: () => void;
};
}
}
export declare class Jqhtml_Component {
static template?: any;
$: any;
args: Record<string, any>;
data: Record<string, any>;
_cid: string;
_ready_state: number;
private _lifecycle_manager;
private _parent;
private _children;
private _destroyed;
private _data_before_render;
private _lifecycle_callbacks;
private _lifecycle_states;
private __loading;
constructor(element?: any, args?: Record<string, any>);
/**
* Render phase - Create DOM structure
* Called top-down (parent before children) when part of lifecycle
* Can also be called directly to just re-render DOM without lifecycle hooks
*
* @param id Optional scoped ID - if provided, delegates to child component's render()
*/
render(id?: string | null): Promise<void>;
/**
* Create phase - Quick setup, prepare UI
* Called bottom-up (children before parent)
*/
create(): Promise<void>;
/**
* Load phase - Fetch data from APIs
* Called bottom-up, fully parallel
* NO DOM MODIFICATIONS ALLOWED IN THIS PHASE
*/
load(): Promise<void>;
/**
* Ready phase - Component fully initialized
* Called bottom-up (children before parent)
*/
ready(): Promise<void>;
/**
* Reinitialize the component - full reset and re-initialization
* Wipes the innerHTML, resets data to empty, and runs full lifecycle
*/
reinitialize(): Promise<void>;
/**
* Reload data - re-fetch data and update the component
* Re-runs on_load(), then renders and calls on_ready()
*/
reload_data(): Promise<void>;
/**
* Destroy the component and cleanup
* Synchronously cleans up component and all children before removal
*/
destroy(): void;
on_render(): void | Promise<void>;
on_create(): void | Promise<void>;
on_load(): Promise<void>;
on_ready(): Promise<void>;
on_destroy(): void | Promise<void>;
/**
* Should component re-render after load?
* By default, only re-renders if data has changed
* Override to control re-rendering behavior
*/
should_rerender(): boolean;
/**
* Get component name for debugging
*/
component_name(): string;
/**
* Emit a jQuery event from component root
*/
emit(event_name: string, data?: any): void;
/**
* Register lifecycle event callback
* Allowed events: 'render', 'create', 'load', 'ready', 'destroy'
* Callbacks fire after the lifecycle method completes
* If the event has already occurred, the callback fires immediately AND registers for future occurrences
*/
on(event_name: string, callback: (component: Jqhtml_Component) => void): this;
/**
* Emit lifecycle event - fires all registered callbacks
* @private
*/
private _emit_lifecycle_event;
/**
* Find element by scoped ID
* @param local_id The local ID (without _cid suffix)
* @returns jQuery element with id="local_id:_cid"
*/
$id(local_id: string): any;
/**
* Get component instance by scoped ID
*/
id(local_id: string): Jqhtml_Component | null;
/**
* Get parent component
*/
parent(): Jqhtml_Component | null;
/**
* Get direct child components
*/
children(): Jqhtml_Component[];
/**
* Find descendant components by CSS selector
*/
find(selector: string): Jqhtml_Component[];
/**
* Find closest ancestor component matching selector
*/
closest(selector: string): Jqhtml_Component | null;
/**
* Get CSS class hierarchy for this component type
*/
static get_class_hierarchy(): string[];
private _generate_cid;
/**
* Flatten instruction array - converts ['_content', [...]] markers to flat array
* Recursively flattens nested content from content() calls
*/
private _flatten_instructions;
private _apply_css_classes;
private _apply_default_attributes;
private _set_attributes;
private _update_debug_attrs;
private _find_parent;
private _log_lifecycle;
private _log_debug;
}
//# sourceMappingURL=component.d.ts.map

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

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

36
node_modules/@jqhtml/core/dist/debug-entry.d.ts generated vendored Executable file
View File

@@ -0,0 +1,36 @@
/**
* JQHTML Debug Module
*
* Optional debugging utilities for development
* Import separately to keep production bundles small:
*
* import { showDebugOverlay } from '@jqhtml/core/debug';
*/
export { DebugOverlay, showDebugOverlay, hideDebugOverlay } from './debug-overlay.js';
export type { DebugOverlayOptions } from './debug-overlay.js';
export { logLifecycle, applyDebugDelay, logDispatch, logInstruction, logDataChange, isSequentialProcessing, handleComponentError, devWarn } from './debug.js';
export interface DebugSettings {
verbose?: boolean;
logCreationReady?: boolean;
logFullLifecycle?: boolean;
logDispatch?: boolean;
logDispatchVerbose?: boolean;
delayAfterComponent?: number;
delayAfterRender?: number;
delayAfterRerender?: number;
sequentialProcessing?: boolean;
flashComponents?: boolean;
flashDuration?: number;
flashColors?: {
create?: string;
render?: string;
ready?: string;
};
showComponentTree?: boolean;
profilePerformance?: boolean;
breakOnError?: boolean;
traceDataFlow?: boolean;
logInstructionProcessing?: boolean;
highlightSlowRenders?: number;
}
//# sourceMappingURL=debug-entry.d.ts.map

1
node_modules/@jqhtml/core/dist/debug-entry.d.ts.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"debug-entry.d.ts","sourceRoot":"","sources":["../src/debug-entry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAG9D,OAAO,EACL,YAAY,EACZ,eAAe,EACf,WAAW,EACX,cAAc,EACd,aAAa,EACb,sBAAsB,EACtB,oBAAoB,EACpB,OAAO,EACR,MAAM,YAAY,CAAC;AAGpB,MAAM,WAAW,aAAa;IAE5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAG7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAG/B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE;QACZ,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IAGF,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B"}

61
node_modules/@jqhtml/core/dist/debug-overlay.d.ts generated vendored Executable file
View File

@@ -0,0 +1,61 @@
/**
* JQHTML Debug Overlay
*
* Independent debug controls using pure jQuery DOM manipulation.
* Does NOT use JQHTML components so it works even when components are broken.
*/
export interface DebugOverlayOptions {
position?: 'top' | 'bottom';
theme?: 'dark' | 'light';
compact?: boolean;
showStatus?: boolean;
autoHide?: boolean;
}
export declare class DebugOverlay {
private static instance;
private $container;
private $statusIndicator;
private options;
private $;
constructor(options?: DebugOverlayOptions);
/**
* Static method to show debug overlay (singleton pattern)
*/
static show(options?: DebugOverlayOptions): DebugOverlay;
/**
* Static method to hide debug overlay
*/
static hide(): void;
/**
* Static method to toggle debug overlay visibility
*/
static toggle(): void;
/**
* Static method to destroy debug overlay
*/
static destroy(): void;
/**
* Display the debug overlay
*/
private display;
/**
* Hide the debug overlay
*/
hide(): void;
/**
* Remove the debug overlay completely
*/
destroy(): void;
/**
* Update the status indicator
*/
updateStatus(mode: string): void;
private createOverlay;
private createStatusIndicator;
private addStyles;
private toggle;
private executeAction;
}
export declare function showDebugOverlay(options?: DebugOverlayOptions): DebugOverlay;
export declare function hideDebugOverlay(): void;
//# sourceMappingURL=debug-overlay.d.ts.map

1
node_modules/@jqhtml/core/dist/debug-overlay.d.ts.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"debug-overlay.d.ts","sourceRoot":"","sources":["../src/debug-overlay.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA8BH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA6B;IACpD,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,OAAO,CAAsB;IACrC,OAAO,CAAC,CAAC,CAAM;gBAEH,OAAO,GAAE,mBAAwB;IAgB7C;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,YAAY;IAQxD;;OAEG;IACH,MAAM,CAAC,IAAI,IAAI,IAAI;IAMnB;;OAEG;IACH,MAAM,CAAC,MAAM,IAAI,IAAI;IAYrB;;OAEG;IACH,MAAM,CAAC,OAAO,IAAI,IAAI;IAOtB;;OAEG;IACH,OAAO,CAAC,OAAO;IAYf;;OAEG;IACH,IAAI,IAAI,IAAI;IASZ;;OAEG;IACH,OAAO,IAAI,IAAI;IAWf;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAOhC,OAAO,CAAC,aAAa;IAiDrB,OAAO,CAAC,qBAAqB;IAqB7B,OAAO,CAAC,SAAS;IAoGjB,OAAO,CAAC,MAAM;IAed,OAAO,CAAC,aAAa;CAoEtB;AAGD,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,YAAY,CAE5E;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC"}

19
node_modules/@jqhtml/core/dist/debug.d.ts generated vendored Executable file
View File

@@ -0,0 +1,19 @@
/**
* JQHTML Debug Module
*
* Provides comprehensive debugging capabilities for JQHTML components
*/
import type { Jqhtml_Component } from './component.js';
/**
* Development warning helper
* Warnings are suppressed in production builds or when JQHTML_SUPPRESS_WARNINGS is set
*/
export declare function devWarn(message: string): void;
export declare function logLifecycle(component: Jqhtml_Component, phase: string, status: 'start' | 'complete'): void;
export declare function applyDebugDelay(phase: 'component' | 'render' | 'rerender'): void;
export declare function logInstruction(type: string, data: any): void;
export declare function logDataChange(component: Jqhtml_Component, property: string, oldValue: any, newValue: any): void;
export declare function logDispatch(url: string, route: any, params: any, verbose?: boolean): void;
export declare function isSequentialProcessing(): boolean;
export declare function handleComponentError(component: Jqhtml_Component, phase: string, error: Error): void;
//# sourceMappingURL=debug.d.ts.map

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

@@ -0,0 +1 @@
{"version":3,"file":"debug.d.ts","sourceRoot":"","sources":["../src/debug.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAQvD;;;GAGG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAY7C;AAuDD,wBAAgB,YAAY,CAAC,SAAS,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,UAAU,GAAG,IAAI,CAmD3G;AAGD,wBAAgB,eAAe,CAAC,KAAK,EAAE,WAAW,GAAG,QAAQ,GAAG,UAAU,GAAG,IAAI,CAqBhF;AAGD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,IAAI,CAK5D;AAGD,wBAAgB,aAAa,CAAC,SAAS,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,GAAG,IAAI,CAM/G;AAUD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAE,OAAe,GAAG,IAAI,CAoBhG;AAGD,wBAAgB,sBAAsB,IAAI,OAAO,CAGhD;AAGD,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAQnG"}

2869
node_modules/@jqhtml/core/dist/index.cjs generated vendored Executable file

File diff suppressed because it is too large Load Diff

1
node_modules/@jqhtml/core/dist/index.cjs.map generated vendored Executable file

File diff suppressed because one or more lines are too long

86
node_modules/@jqhtml/core/dist/index.d.ts generated vendored Executable file
View File

@@ -0,0 +1,86 @@
/**
* JQHTML v2 Core Runtime
*
* Main entry point for the JQHTML runtime library
*/
export { Jqhtml_Component } from './component.js';
export { LifecycleManager } from './lifecycle-manager.js';
export type { LifecyclePhase } from './lifecycle-manager.js';
export { register_component, get_component_class, register_template, get_template, get_template_by_class, create_component, has_component, get_component_names, get_registered_templates, list_components } from './component-registry.js';
export type { ComponentConstructor, TemplateFunction, TemplateDefinition } from './component-registry.js';
export { process_instructions, extract_slots } from './instruction-processor.js';
export type { Instruction, TagInstruction, ComponentInstruction, SlotInstruction } from './instruction-processor.js';
export { render_template, escape_html } from './template-renderer.js';
export { logLifecycle, applyDebugDelay, logDispatch, logInstruction, logDataChange, isSequentialProcessing, handleComponentError, devWarn } from './debug.js';
export { DebugOverlay, showDebugOverlay, hideDebugOverlay } from './debug-overlay.js';
export type { DebugOverlayOptions } from './debug-overlay.js';
import { init_jquery_plugin } from './jquery-plugin.js';
export { init_jquery_plugin };
export declare function init(jQuery?: any): void;
export { LifecycleManager as Jqhtml_LifecycleManager } from './lifecycle-manager.js';
import { Jqhtml_Component } from './component.js';
import { LifecycleManager } from './lifecycle-manager.js';
import { register_component, get_component_class, register_template, get_template, get_template_by_class, create_component, has_component, get_component_names, get_registered_templates, list_components } from './component-registry.js';
import { process_instructions, extract_slots } from './instruction-processor.js';
import { render_template, escape_html } from './template-renderer.js';
import { DebugOverlay } from './debug-overlay.js';
import './jquery-plugin.js';
export declare const version = "__VERSION__";
export interface DebugSettings {
verbose?: boolean;
logCreationReady?: boolean;
logFullLifecycle?: boolean;
logDispatch?: boolean;
logDispatchVerbose?: boolean;
delayAfterComponent?: number;
delayAfterRender?: number;
delayAfterRerender?: number;
sequentialProcessing?: boolean;
flashComponents?: boolean;
flashDuration?: number;
flashColors?: {
create?: string;
render?: string;
ready?: string;
};
showComponentTree?: boolean;
profilePerformance?: boolean;
breakOnError?: boolean;
traceDataFlow?: boolean;
logInstructionProcessing?: boolean;
highlightSlowRenders?: number;
}
declare const jqhtml: {
Jqhtml_Component: typeof Jqhtml_Component;
LifecycleManager: typeof LifecycleManager;
register_component: typeof register_component;
get_component_class: typeof get_component_class;
register_template: typeof register_template;
get_template: typeof get_template;
get_template_by_class: typeof get_template_by_class;
create_component: typeof create_component;
has_component: typeof has_component;
get_component_names: typeof get_component_names;
get_registered_templates: typeof get_registered_templates;
list_components: typeof list_components;
process_instructions: typeof process_instructions;
extract_slots: typeof extract_slots;
render_template: typeof render_template;
escape_html: typeof escape_html;
__version: string;
debug: DebugSettings & {
enabled: boolean;
verbose: boolean;
};
setDebugSettings(settings: DebugSettings): void;
enableDebugMode(level?: "basic" | "full"): void;
clearDebugSettings(): void;
showDebugOverlay(options?: any): DebugOverlay;
hideDebugOverlay(): void;
DebugOverlay: typeof DebugOverlay;
installGlobals(): void;
_version(): any;
version(): string;
};
export default jqhtml;
//# sourceMappingURL=index.d.ts.map

1
node_modules/@jqhtml/core/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":"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"}

2834
node_modules/@jqhtml/core/dist/index.js generated vendored Executable file

File diff suppressed because it is too large Load Diff

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

File diff suppressed because one or more lines are too long

27
node_modules/@jqhtml/core/dist/instruction-processor.d.ts generated vendored Executable file
View File

@@ -0,0 +1,27 @@
/**
* JQHTML v2 Instruction Processor
*
* Processes instruction arrays generated by the parser
* Uses v1's HTML string building approach for performance
*/
import { Jqhtml_Component } from './component.js';
export interface TagInstruction {
tag: [string, Record<string, any>, boolean];
}
export interface ComponentInstruction {
comp: [string, Record<string, any>] | [string, Record<string, any>, (context: any) => [any[], Jqhtml_Component]];
}
export interface SlotInstruction {
slot: [string, Record<string, any>, (context: any) => [any[], Jqhtml_Component]];
}
export type Instruction = TagInstruction | ComponentInstruction | SlotInstruction | string;
/**
* Process an array of instructions and append to target
* Uses v1 approach: build HTML string, set innerHTML, then initialize
*/
export declare function process_instructions(instructions: Instruction[], target: any, context: Jqhtml_Component, slots?: Record<string, SlotInstruction>): Promise<void>;
/**
* Utility to extract slots from instructions
*/
export declare function extract_slots(instructions: Instruction[]): Record<string, SlotInstruction>;
//# sourceMappingURL=instruction-processor.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"instruction-processor.d.ts","sourceRoot":"","sources":["../src/instruction-processor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAIlD,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,GAAG,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC;CAClH;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,GAAG,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC;CAClF;AAED,MAAM,MAAM,WAAW,GAAG,cAAc,GAAG,oBAAoB,GAAG,eAAe,GAAG,MAAM,CAAC;AAqB3F;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,YAAY,EAAE,WAAW,EAAE,EAC3B,MAAM,EAAE,GAAG,EACX,OAAO,EAAE,gBAAgB,EACzB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,GACtC,OAAO,CAAC,IAAI,CAAC,CAuCf;AAwXD;;GAEG;AACH,wBAAgB,aAAa,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAW1F"}

2839
node_modules/@jqhtml/core/dist/jqhtml-core.esm.js generated vendored Executable file

File diff suppressed because it is too large Load Diff

1
node_modules/@jqhtml/core/dist/jqhtml-core.esm.js.map generated vendored Executable file

File diff suppressed because one or more lines are too long

575
node_modules/@jqhtml/core/dist/jqhtml-debug.esm.js generated vendored Executable file
View File

@@ -0,0 +1,575 @@
/**
* JQHTML Debug Overlay
*
* Independent debug controls using pure jQuery DOM manipulation.
* Does NOT use JQHTML components so it works even when components are broken.
*/
// Get global jQuery
function getJQuery() {
if (typeof window !== 'undefined' && window.$) {
return window.$;
}
if (typeof window !== 'undefined' && window.jQuery) {
return window.jQuery;
}
throw new Error('FATAL: jQuery is not defined. jQuery must be loaded before using JQHTML. ' +
'Add <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> before loading JQHTML.');
}
// Get global jqhtml object
function getJqhtml$1() {
if (typeof window !== 'undefined' && window.jqhtml) {
return window.jqhtml;
}
if (typeof globalThis !== 'undefined' && globalThis.jqhtml) {
return globalThis.jqhtml;
}
throw new Error('FATAL: window.jqhtml is not defined. The JQHTML runtime must be loaded before using JQHTML components. ' +
'Ensure @jqhtml/core is imported and initialized before attempting to use debug features.');
}
class DebugOverlay {
constructor(options = {}) {
this.$container = null;
this.$statusIndicator = null;
this.$ = getJQuery();
if (!this.$) {
throw new Error('jQuery is required for DebugOverlay');
}
this.options = {
position: 'bottom',
theme: 'dark',
compact: false,
showStatus: true,
autoHide: false,
...options
};
}
/**
* Static method to show debug overlay (singleton pattern)
*/
static show(options) {
if (!DebugOverlay.instance) {
DebugOverlay.instance = new DebugOverlay(options);
}
DebugOverlay.instance.display();
return DebugOverlay.instance;
}
/**
* Static method to hide debug overlay
*/
static hide() {
if (DebugOverlay.instance) {
DebugOverlay.instance.hide();
}
}
/**
* Static method to toggle debug overlay visibility
*/
static toggle() {
if (DebugOverlay.instance && DebugOverlay.instance.$container) {
if (DebugOverlay.instance.$container.is(':visible')) {
DebugOverlay.hide();
}
else {
DebugOverlay.instance.display();
}
}
else {
DebugOverlay.show();
}
}
/**
* Static method to destroy debug overlay
*/
static destroy() {
if (DebugOverlay.instance) {
DebugOverlay.instance.destroy();
DebugOverlay.instance = null;
}
}
/**
* Display the debug overlay
*/
display() {
if (this.$container) {
this.$container.show();
return;
}
this.createOverlay();
if (this.options.showStatus) {
this.createStatusIndicator();
}
}
/**
* Hide the debug overlay
*/
hide() {
if (this.$container) {
this.$container.hide();
}
if (this.$statusIndicator) {
this.$statusIndicator.hide();
}
}
/**
* Remove the debug overlay completely
*/
destroy() {
if (this.$container) {
this.$container.remove();
this.$container = null;
}
if (this.$statusIndicator) {
this.$statusIndicator.remove();
this.$statusIndicator = null;
}
}
/**
* Update the status indicator
*/
updateStatus(mode) {
if (!this.$statusIndicator)
return;
this.$statusIndicator.text('Debug: ' + mode);
this.$statusIndicator.attr('class', 'jqhtml-debug-status' + (mode !== 'Off' ? ' active' : ''));
}
createOverlay() {
// Add styles first
this.addStyles();
// Create container using jQuery
this.$container = this.$('<div>')
.addClass(`jqhtml-debug-overlay ${this.options.theme} ${this.options.position}`);
// Create content structure
const $content = this.$('<div>').addClass('jqhtml-debug-content');
const $controls = this.$('<div>').addClass('jqhtml-debug-controls');
// Add title
const $title = this.$('<span>')
.addClass('jqhtml-debug-title')
.html('<strong>🐛 JQHTML Debug:</strong>');
$controls.append($title);
// Create buttons
const buttons = [
{ text: 'Slow Motion + Flash', action: 'enableSlowMotionDebug', class: 'success' },
{ text: 'Basic Debug', action: 'enableBasicDebug', class: '' },
{ text: 'Full Debug', action: 'enableFullDebug', class: '' },
{ text: 'Sequential', action: 'enableSequentialMode', class: '' },
{ text: 'Clear Debug', action: 'clearAllDebug', class: 'danger' },
{ text: 'Settings', action: 'showDebugInfo', class: '' }
];
buttons.forEach(btn => {
const $button = this.$('<button>')
.text(btn.text)
.addClass('jqhtml-debug-btn' + (btn.class ? ` ${btn.class}` : ''))
.on('click', () => this.executeAction(btn.action));
$controls.append($button);
});
// Add minimize/close button
const $toggleBtn = this.$('<button>')
.text(this.options.compact ? '▼' : '▲')
.addClass('jqhtml-debug-toggle')
.on('click', () => this.toggle());
$controls.append($toggleBtn);
// Assemble and add to page
$content.append($controls);
this.$container.append($content);
this.$('body').append(this.$container);
}
createStatusIndicator() {
this.$statusIndicator = this.$('<div>')
.addClass('jqhtml-debug-status')
.text('Debug: Off')
.css({
position: 'fixed',
top: '10px',
right: '10px',
background: '#2c3e50',
color: 'white',
padding: '5px 10px',
borderRadius: '4px',
fontSize: '0.75rem',
zIndex: '10001',
opacity: '0.8',
fontFamily: 'monospace'
});
this.$('body').append(this.$statusIndicator);
}
addStyles() {
// Check if styles already exist
if (this.$('#jqhtml-debug-styles').length > 0)
return;
// Create and inject CSS using jQuery - concatenated strings for better minification
const $style = this.$('<style>')
.attr('id', 'jqhtml-debug-styles')
.text('.jqhtml-debug-overlay {' +
'position: fixed;' +
'left: 0;' +
'right: 0;' +
'z-index: 10000;' +
'font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace;' +
'font-size: 0.8rem;' +
'box-shadow: 0 2px 10px rgba(0,0,0,0.2);' +
'}' +
'.jqhtml-debug-overlay.top {' +
'top: 0;' +
'}' +
'.jqhtml-debug-overlay.bottom {' +
'bottom: 0;' +
'}' +
'.jqhtml-debug-overlay.dark {' +
'background: #34495e;' +
'color: #ecf0f1;' +
'}' +
'.jqhtml-debug-overlay.light {' +
'background: #f8f9fa;' +
'color: #333;' +
'border-bottom: 1px solid #dee2e6;' +
'}' +
'.jqhtml-debug-content {' +
'padding: 0.5rem 1rem;' +
'}' +
'.jqhtml-debug-controls {' +
'display: flex;' +
'flex-wrap: wrap;' +
'gap: 8px;' +
'align-items: center;' +
'}' +
'.jqhtml-debug-title {' +
'margin-right: 10px;' +
'font-weight: bold;' +
'}' +
'.jqhtml-debug-btn {' +
'padding: 4px 8px;' +
'border: none;' +
'border-radius: 3px;' +
'background: #3498db;' +
'color: white;' +
'cursor: pointer;' +
'font-size: 0.75rem;' +
'transition: background 0.2s;' +
'}' +
'.jqhtml-debug-btn:hover {' +
'background: #2980b9;' +
'}' +
'.jqhtml-debug-btn.success {' +
'background: #27ae60;' +
'}' +
'.jqhtml-debug-btn.success:hover {' +
'background: #229954;' +
'}' +
'.jqhtml-debug-btn.danger {' +
'background: #e74c3c;' +
'}' +
'.jqhtml-debug-btn.danger:hover {' +
'background: #c0392b;' +
'}' +
'.jqhtml-debug-toggle {' +
'padding: 4px 8px;' +
'border: none;' +
'border-radius: 3px;' +
'background: #7f8c8d;' +
'color: white;' +
'cursor: pointer;' +
'font-size: 0.75rem;' +
'margin-left: auto;' +
'}' +
'.jqhtml-debug-toggle:hover {' +
'background: #6c7b7d;' +
'}' +
'.jqhtml-debug-status.active {' +
'background: #27ae60 !important;' +
'}' +
'@media (max-width: 768px) {' +
'.jqhtml-debug-controls {' +
'flex-direction: column;' +
'align-items: flex-start;' +
'}' +
'.jqhtml-debug-title {' +
'margin-bottom: 5px;' +
'}' +
'}');
this.$('head').append($style);
}
toggle() {
// Toggle between compact and full view
this.options.compact = !this.options.compact;
const $toggleBtn = this.$container.find('.jqhtml-debug-toggle');
$toggleBtn.text(this.options.compact ? '▼' : '▲');
const $buttons = this.$container.find('.jqhtml-debug-btn');
if (this.options.compact) {
$buttons.hide();
}
else {
$buttons.show();
}
}
executeAction(action) {
const jqhtml = getJqhtml$1();
if (!jqhtml) {
console.warn('JQHTML not available - make sure it\'s loaded and exposed globally');
return;
}
switch (action) {
case 'enableSlowMotionDebug':
jqhtml.setDebugSettings({
logFullLifecycle: true,
sequentialProcessing: true,
delayAfterComponent: 150,
delayAfterRender: 200,
delayAfterRerender: 250,
flashComponents: true,
flashDuration: 800,
flashColors: {
create: '#3498db',
render: '#27ae60',
ready: '#9b59b6'
},
profilePerformance: true,
highlightSlowRenders: 30,
logDispatch: true
});
this.updateStatus('Slow Motion');
console.log('🐛 Slow Motion Debug Mode Enabled');
break;
case 'enableBasicDebug':
jqhtml.enableDebugMode('basic');
this.updateStatus('Basic');
console.log('🐛 Basic Debug Mode Enabled');
break;
case 'enableFullDebug':
jqhtml.enableDebugMode('full');
this.updateStatus('Full');
console.log('🐛 Full Debug Mode Enabled');
break;
case 'enableSequentialMode':
jqhtml.setDebugSettings({
logCreationReady: true,
sequentialProcessing: true,
flashComponents: true,
profilePerformance: true
});
this.updateStatus('Sequential');
console.log('🐛 Sequential Processing Mode Enabled');
break;
case 'clearAllDebug':
jqhtml.clearDebugSettings();
this.updateStatus('Off');
console.log('🐛 All Debug Modes Disabled');
break;
case 'showDebugInfo':
const settings = JSON.stringify(jqhtml.debug, null, 2);
console.log('🐛 Current Debug Settings:', settings);
alert('Debug settings logged to console:\n\n' + (Object.keys(jqhtml.debug).length > 0 ? settings : 'No debug settings active'));
break;
}
}
}
DebugOverlay.instance = null;
// Simplified global convenience functions that use static methods
function showDebugOverlay(options) {
return DebugOverlay.show(options);
}
function hideDebugOverlay() {
DebugOverlay.hide();
}
// Auto-initialize if debug query parameter is present
if (typeof window !== 'undefined') {
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.get('debug') === 'true' || urlParams.get('jqhtml-debug') === 'true') {
document.addEventListener('DOMContentLoaded', () => {
DebugOverlay.show();
});
}
}
/**
* JQHTML Debug Module
*
* Provides comprehensive debugging capabilities for JQHTML components
*/
// Global debug state
let performanceMetrics = new Map();
/**
* Development warning helper
* Warnings are suppressed in production builds or when JQHTML_SUPPRESS_WARNINGS is set
*/
function devWarn(message) {
// Check if warnings are suppressed
if (typeof window !== 'undefined' && window.JQHTML_SUPPRESS_WARNINGS) {
return;
}
// Check if in production mode
if (typeof process !== 'undefined' && process.env && process.env.NODE_ENV === 'production') {
return;
}
console.warn(`[JQHTML Dev Warning] ${message}`);
}
// Get global jqhtml object
function getJqhtml() {
if (typeof window !== 'undefined' && window.jqhtml) {
return window.jqhtml;
}
// Fallback: try to get from global if available
if (typeof globalThis !== 'undefined' && globalThis.jqhtml) {
return globalThis.jqhtml;
}
throw new Error('FATAL: window.jqhtml is not defined. The JQHTML runtime must be loaded before using debug features. ' +
'Import and initialize @jqhtml/core before attempting to use debug functionality.');
}
// Visual flash effect
function flashComponent(component, eventType) {
const jqhtml = getJqhtml();
if (!jqhtml?.debug?.flashComponents)
return;
const duration = jqhtml.debug.flashDuration || 500;
const colors = jqhtml.debug.flashColors || {};
const color = colors[eventType] || (eventType === 'create' ? '#3498db' :
eventType === 'render' ? '#27ae60' :
'#9b59b6');
// Store original border
const originalBorder = component.$.css('border');
// Apply flash border
component.$.css({
'border': `2px solid ${color}`,
'transition': `border ${duration}ms ease-out`
});
// Remove after duration
setTimeout(() => {
component.$.css('border', originalBorder || '');
}, duration);
}
// Log lifecycle event
function logLifecycle(component, phase, status) {
const jqhtml = getJqhtml();
if (!jqhtml?.debug)
return;
const shouldLog = jqhtml.debug.logFullLifecycle ||
(jqhtml.debug.logCreationReady && (phase === 'create' || phase === 'ready'));
if (!shouldLog)
return;
const componentName = component.constructor.name;
const timestamp = new Date().toISOString();
const prefix = `[JQHTML ${timestamp}]`;
if (status === 'start') {
console.log(`${prefix} ${componentName}#${component._cid}${phase} starting...`);
// Start performance tracking
if (jqhtml.debug.profilePerformance) {
performanceMetrics.set(`${component._cid}_${phase}`, Date.now());
}
}
else {
let message = `${prefix} ${componentName}#${component._cid}${phase} complete`;
// Add performance data
if (jqhtml.debug.profilePerformance) {
const startTime = performanceMetrics.get(`${component._cid}_${phase}`);
if (startTime) {
const duration = Date.now() - startTime;
message += ` (${duration}ms)`;
// Highlight slow renders
if (phase === 'render' && jqhtml.debug.highlightSlowRenders &&
duration > jqhtml.debug.highlightSlowRenders) {
console.warn(`${prefix} SLOW RENDER: ${componentName}#${component._cid} took ${duration}ms`);
component.$.css('outline', '2px dashed red');
}
}
}
console.log(message);
// Visual feedback
if (jqhtml.debug.flashComponents && (phase === 'create' || phase === 'render' || phase === 'ready')) {
flashComponent(component, phase);
}
}
// Update component tree if enabled
if (jqhtml.debug.showComponentTree) {
updateComponentTree();
}
}
// Apply delays based on lifecycle phase
function applyDebugDelay(phase) {
const jqhtml = getJqhtml();
if (!jqhtml?.debug)
return;
let delayMs = 0;
switch (phase) {
case 'component':
delayMs = jqhtml.debug.delayAfterComponent || 0;
break;
case 'render':
delayMs = jqhtml.debug.delayAfterRender || 0;
break;
case 'rerender':
delayMs = jqhtml.debug.delayAfterRerender || 0;
break;
}
if (delayMs > 0) {
console.log(`[JQHTML Debug] Applying ${delayMs}ms delay after ${phase}`);
}
}
// Log instruction processing
function logInstruction(type, data) {
const jqhtml = getJqhtml();
if (!jqhtml?.debug?.logInstructionProcessing)
return;
console.log(`[JQHTML Instruction] ${type}:`, data);
}
// Log data changes
function logDataChange(component, property, oldValue, newValue) {
const jqhtml = getJqhtml();
if (!jqhtml?.debug?.traceDataFlow)
return;
console.log(`[JQHTML Data] ${component.constructor.name}#${component._cid}.data.${property}:`, { old: oldValue, new: newValue });
}
// Update component tree visualization
function updateComponentTree() {
// This would update a debug panel if implemented
// For now, just log the tree structure periodically
console.log('[JQHTML Tree] Component hierarchy updated');
}
// Router dispatch logging
function logDispatch(url, route, params, verbose = false) {
const jqhtml = getJqhtml();
if (!jqhtml?.debug)
return;
const shouldLog = jqhtml.debug.logDispatch || jqhtml.debug.logDispatchVerbose;
if (!shouldLog)
return;
const isVerbose = jqhtml.debug.logDispatchVerbose || verbose;
if (isVerbose) {
console.group(`[JQHTML Router] Dispatching: ${url}`);
console.log('Matched route:', route);
console.log('Extracted params:', params);
console.log('Route component:', route.component);
console.log('Route layout:', route.layout);
console.log('Route meta:', route.meta);
console.groupEnd();
}
else {
console.log(`[JQHTML Router] ${url}${route.component} (params: ${JSON.stringify(params)})`);
}
}
// Check if sequential processing is enabled
function isSequentialProcessing() {
const jqhtml = getJqhtml();
return jqhtml?.debug?.sequentialProcessing || false;
}
// Error handling with break on error
function handleComponentError(component, phase, error) {
const jqhtml = getJqhtml();
console.error(`[JQHTML Error] ${component.constructor.name}#${component._cid} failed in ${phase}:`, error);
if (jqhtml?.debug?.breakOnError) {
debugger; // This will pause execution in dev tools
}
}
// Additional debug suggestions that could be implemented:
//
// 1. Component Inspector - Click on any component to see its data/args/state
// 2. Time Travel Debugging - Record state changes and replay them
// 3. Network Request Tracking - Log all AJAX calls made during load()
// 4. Memory Leak Detection - Track component creation/destruction
// 5. Template Compilation Debugging - Show compiled template functions
// 6. Event Flow Visualization - Show event bubbling through components
// 7. Dependency Graph - Show which components depend on which data
// 8. Hot Reload Support - Reload components without losing state
// 9. Performance Budgets - Warn when components exceed size/time limits
// 10. Accessibility Auditing - Check for missing ARIA attributes
export { DebugOverlay, applyDebugDelay, devWarn, handleComponentError, hideDebugOverlay, isSequentialProcessing, logDataChange, logDispatch, logInstruction, logLifecycle, showDebugOverlay };
//# sourceMappingURL=jqhtml-debug.esm.js.map

1
node_modules/@jqhtml/core/dist/jqhtml-debug.esm.js.map generated vendored Executable file

File diff suppressed because one or more lines are too long

21
node_modules/@jqhtml/core/dist/jquery-plugin.d.ts generated vendored Executable file
View File

@@ -0,0 +1,21 @@
/**
* JQHTML v2 jQuery Plugin
*
* Extends jQuery with component method:
* - $(el).component() - Get component instance
* - $(el).component(ComponentClass, args) - Create component
*/
import { Jqhtml_Component } from './component.js';
import type { ComponentConstructor } from './component-registry.js';
declare global {
interface JQuery {
/**
* Get or create a component on this element
*/
component(): Jqhtml_Component | null;
component(ComponentClass: ComponentConstructor, args?: Record<string, any>): Jqhtml_Component;
component(componentName: string, args?: Record<string, any>): Jqhtml_Component;
}
}
export declare function init_jquery_plugin(jQuery: any): void;
//# sourceMappingURL=jquery-plugin.d.ts.map

1
node_modules/@jqhtml/core/dist/jquery-plugin.d.ts.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"jquery-plugin.d.ts","sourceRoot":"","sources":["../src/jquery-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAQpE,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd;;WAEG;QACH,SAAS,IAAI,gBAAgB,GAAG,IAAI,CAAC;QACrC,SAAS,CAAC,cAAc,EAAE,oBAAoB,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,gBAAgB,CAAC;QAC9F,SAAS,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,gBAAgB,CAAC;KAChF;CACF;AAGD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI,CA2JpD"}

70
node_modules/@jqhtml/core/dist/lifecycle-manager.d.ts generated vendored Executable file
View File

@@ -0,0 +1,70 @@
/**
* JQHTML v2 Lifecycle Manager
*
* Coordinates phase batching across all components
* Implements the v2 lifecycle specification:
* 1. render - Create DOM structure (top-down)
* 2. create - Quick setup, hide elements, prepare UI (bottom-up)
* 3. load - Fetch data from APIs (bottom-up, parallelizable)
* 4. ready - Component fully initialized (bottom-up)
*/
import type { Jqhtml_Component } from './component.js';
export type LifecyclePhase = 'render' | 'create' | 'load' | 'ready';
export declare class LifecycleManager {
private static instance;
private active_components;
private phase_queues;
private current_phase;
private processing;
constructor();
static get_instance(): LifecycleManager;
/**
* Register a component for lifecycle processing
*/
register_component(component: Jqhtml_Component): void;
/**
* Unregister a component (called on destroy)
*/
unregister_component(component: Jqhtml_Component): void;
/**
* Process lifecycle phases for all queued components
*/
private process_queues;
/**
* Process a single lifecycle phase
*/
private process_phase;
/**
* Execute a lifecycle phase on a component
*/
private execute_phase;
/**
* Sort components by hierarchy for proper phase execution order
*/
private sort_components_by_hierarchy;
/**
* Process bottom-up with parallel sibling execution
*/
private process_bottom_up_parallel;
/**
* Process top-down sequentially
*/
private process_top_down;
/**
* Group components by their depth in the hierarchy
*/
private group_by_depth;
/**
* Find the parent component of a given component
*/
private find_parent_component;
/**
* Catch up a component to the current lifecycle phase
*/
private catch_up_component;
/**
* Wait for all components to reach ready state
*/
wait_for_ready(): Promise<void>;
}
//# sourceMappingURL=lifecycle-manager.d.ts.map

1
node_modules/@jqhtml/core/dist/lifecycle-manager.d.ts.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"lifecycle-manager.d.ts","sourceRoot":"","sources":["../src/lifecycle-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGvD,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAepE,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAmB;IAC1C,OAAO,CAAC,iBAAiB,CAAoC;IAC7D,OAAO,CAAC,YAAY,CAAyD;IAC7E,OAAO,CAAC,aAAa,CAA+B;IACpD,OAAO,CAAC,UAAU,CAAkB;;IASpC,MAAM,CAAC,YAAY,IAAI,gBAAgB;IAOvC;;OAEG;IACH,kBAAkB,CAAC,SAAS,EAAE,gBAAgB,GAAG,IAAI;IAarD;;OAEG;IACH,oBAAoB,CAAC,SAAS,EAAE,gBAAgB,GAAG,IAAI;IASvD;;OAEG;YACW,cAAc;IAe5B;;OAEG;YACW,aAAa;IA6C3B;;OAEG;YACW,aAAa;IA+C3B;;OAEG;IACH,OAAO,CAAC,4BAA4B;IA+CpC;;OAEG;YACW,0BAA0B;IAiBxC;;OAEG;YACW,gBAAgB;IAS9B;;OAEG;IACH,OAAO,CAAC,cAAc;IAuBtB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAiB7B;;OAEG;YACW,kBAAkB;IAehC;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;CAMtC"}

17
node_modules/@jqhtml/core/dist/template-renderer.d.ts generated vendored Executable file
View File

@@ -0,0 +1,17 @@
/**
* JQHTML v2 Template Renderer
*
* Connects compiled templates to components
* Processes instruction arrays and handles bindings
*/
import type { Jqhtml_Component } from './component.js';
/**
* Render a template for a component
* Templates are functions that return [instructions, context] tuples
*/
export declare function render_template(component: Jqhtml_Component, template_fn?: Function): Promise<void>;
/**
* Helper to escape HTML for safe output
*/
export declare function escape_html(str: string): string;
//# sourceMappingURL=template-renderer.d.ts.map

1
node_modules/@jqhtml/core/dist/template-renderer.d.ts.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"template-renderer.d.ts","sourceRoot":"","sources":["../src/template-renderer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AA2EvD;;;GAGG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,gBAAgB,EAC3B,WAAW,CAAC,EAAE,QAAQ,GACrB,OAAO,CAAC,IAAI,CAAC,CAqDf;AA8KD;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI/C"}

View File

@@ -0,0 +1,339 @@
# JQHTML Laravel Integration
The JQHTML Laravel Bridge is included with the `@jqhtml/core` npm package, making it easy to integrate JQHTML error handling into your Laravel application without requiring a separate Composer package.
## Installation
### Step 1: Install JQHTML via npm
```bash
npm install @jqhtml/core
```
### Step 2: Load the Laravel Bridge in your Laravel application
#### Option A: In `AppServiceProvider` (Recommended)
Add to `app/Providers/AppServiceProvider.php`:
```php
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register()
{
// Load JQHTML Laravel Bridge from node_modules
$jqhtmlBridge = base_path('node_modules/@jqhtml/core/laravel-bridge/autoload.php');
if (file_exists($jqhtmlBridge)) {
require_once $jqhtmlBridge;
}
}
}
```
#### Option B: In `composer.json` autoload
Add to your `composer.json`:
```json
{
"autoload": {
"files": [
"node_modules/@jqhtml/core/laravel-bridge/autoload.php"
]
}
}
```
Then run:
```bash
composer dump-autoload
```
#### Option C: Manual inclusion in `bootstrap/app.php`
For Laravel 11+, add to `bootstrap/app.php`:
```php
use Illuminate\Foundation\Application;
return Application::configure(basePath: dirname(__DIR__))
->withProviders([
// After creating the app, load JQHTML
function ($app) {
$jqhtmlBridge = base_path('node_modules/@jqhtml/core/laravel-bridge/autoload.php');
if (file_exists($jqhtmlBridge)) {
require_once $jqhtmlBridge;
}
}
])
->create();
```
## Configuration
### Publish the configuration file (optional)
Create `config/jqhtml.php`:
```php
<?php
return [
'source_maps_path' => storage_path('jqhtml-sourcemaps'),
'show_source_context' => env('APP_DEBUG', false),
'context_lines' => 5,
'cache_compiled' => !env('APP_DEBUG', false),
'compiled_path' => storage_path('jqhtml-compiled'),
'enable_source_maps' => env('APP_DEBUG', false),
'source_map_mode' => 'external', // 'inline', 'external', or 'both'
];
```
## Usage
### Basic Error Handling
The bridge automatically catches and formats JQHTML template errors. When a JQHTML error occurs, it will be displayed with:
- Template file location with line and column numbers
- Source code context with error highlighting
- Helpful suggestions for common mistakes
- Source map resolution (if available)
### In Your Blade Templates
If you're compiling JQHTML templates and want to catch errors:
```php
use Jqhtml\LaravelBridge\JqhtmlException;
Route::post('/compile-template', function (Request $request) {
$templatePath = resource_path('jqhtml/' . $request->input('template'));
// Use Node.js to compile (via shell_exec, Process, etc.)
$result = shell_exec("node compile-jqhtml.js " . escapeshellarg($templatePath));
$data = json_decode($result, true);
if (!$data['success']) {
// Create exception from JS error data
throw JqhtmlException::createFromJsError($data['error']);
}
return response()->json(['compiled' => $data['code']]);
});
```
### Middleware Setup
Add to your middleware groups in `app/Http/Kernel.php`:
```php
protected $middlewareGroups = [
'web' => [
// ... other middleware
\Jqhtml\LaravelBridge\Middleware\JqhtmlErrorMiddleware::class,
],
];
```
### Manual Exception Handling
```php
use Jqhtml\LaravelBridge\JqhtmlException;
try {
// Your JQHTML compilation or execution
$compiled = compileJqhtmlTemplate($source);
} catch (\Exception $e) {
// Wrap in JQHTML exception for better display
throw new JqhtmlException(
$e->getMessage(),
'templates/my-template.jqhtml',
$lineNumber,
$columnNumber,
$sourceCode,
'Check your template syntax'
);
}
```
## Node.js Integration
### Compilation Script
Create `compile-jqhtml.js` in your Laravel project root:
```javascript
#!/usr/bin/env node
import { parse, generate } from '@jqhtml/core';
import fs from 'fs';
const templatePath = process.argv[2];
try {
const source = fs.readFileSync(templatePath, 'utf8');
const ast = parse(source, templatePath);
const { code, map } = generate(ast, { sourceMap: true });
console.log(JSON.stringify({
success: true,
code,
map
}));
} catch (error) {
// Format error for Laravel
console.log(JSON.stringify({
success: false,
error: {
message: error.message,
filename: error.filename || templatePath,
line: error.line,
column: error.column,
source: error.source,
suggestion: error.suggestion
}
}));
process.exit(1);
}
```
### Using with Laravel Mix/Vite
In `vite.config.js`:
```javascript
import { defineConfig } from 'vite';
import { parse, generate } from '@jqhtml/core';
export default defineConfig({
plugins: [
{
name: 'jqhtml',
transform(source, id) {
if (id.endsWith('.jqhtml')) {
try {
const ast = parse(source, id);
const { code, map } = generate(ast, { sourceMap: true });
return { code, map };
} catch (error) {
// Error will be caught by Laravel bridge
throw error;
}
}
}
}
]
});
```
## API Endpoint Example
Create an API endpoint for compiling JQHTML templates:
```php
// routes/api.php
use Jqhtml\LaravelBridge\JqhtmlException;
Route::post('/api/jqhtml/compile', function (Request $request) {
$template = $request->input('template');
$filename = $request->input('filename', 'template.jqhtml');
// Execute Node.js compilation
$process = new Process([
'node',
base_path('compile-jqhtml.js'),
'--stdin'
]);
$process->setInput(json_encode([
'template' => $template,
'filename' => $filename
]));
$process->run();
if (!$process->isSuccessful()) {
$output = json_decode($process->getOutput(), true);
if (isset($output['error'])) {
throw JqhtmlException::createFromJsError($output['error']);
}
throw new \Exception('Compilation failed');
}
return response()->json(json_decode($process->getOutput(), true));
});
```
## Laravel Ignition Integration
The bridge automatically integrates with Laravel Ignition (if installed) to provide enhanced error display. No additional configuration needed.
## Troubleshooting
### Bridge not loading
Ensure `node_modules/@jqhtml/core` exists:
```bash
ls -la node_modules/@jqhtml/core/laravel-bridge/
```
### Class not found errors
Clear Laravel's cache:
```bash
php artisan cache:clear
php artisan config:clear
composer dump-autoload
```
### Source maps not working
Ensure the source map directory exists and is writable:
```bash
mkdir -p storage/jqhtml-sourcemaps
chmod 755 storage/jqhtml-sourcemaps
```
## Directory Structure
After installation, your Laravel project will have:
```
your-laravel-project/
├── node_modules/
│ └── @jqhtml/
│ └── core/
│ ├── dist/ # JavaScript runtime
│ └── laravel-bridge/ # PHP integration
│ ├── src/ # PHP classes
│ ├── config/ # Laravel config
│ └── autoload.php # Autoloader
├── config/
│ └── jqhtml.php # Your config (optional)
├── storage/
│ ├── jqhtml-compiled/ # Compiled templates
│ └── jqhtml-sourcemaps/ # Source maps
└── compile-jqhtml.js # Node compilation script
```
## Benefits of This Approach
1. **Single Package**: Everything comes from npm, no Composer package needed
2. **Version Sync**: PHP and JS code always match versions
3. **Simple Updates**: Just `npm update @jqhtml/core`
4. **No Private Packagist**: No need for private Composer repositories
5. **CI/CD Friendly**: Works with standard Node.js deployment pipelines
## License
MIT - Same as @jqhtml/core

242
node_modules/@jqhtml/core/laravel-bridge/README.md generated vendored Executable file
View File

@@ -0,0 +1,242 @@
# JQHTML Laravel Bridge
Laravel integration package for JQHTML template error reporting with source map support.
## Installation
```bash
composer require jqhtml/laravel-bridge
```
## Configuration
Publish the configuration file:
```bash
php artisan vendor:publish --tag=jqhtml-config
```
## Usage
### Basic Setup
The package automatically registers its service provider and exception handler extensions. No additional setup is required for basic functionality.
### Error Handling
When a JQHTML template error occurs, the package will:
1. Parse the error message to extract template location information
2. Load source maps if available
3. Format the error for Laravel's exception handler
4. Display enhanced error information in debug mode
### Middleware
To automatically catch and format JQHTML errors, add the middleware to your route groups:
```php
// In app/Http/Kernel.php
protected $middlewareGroups = [
'web' => [
// ...
\Jqhtml\LaravelBridge\Middleware\JqhtmlErrorMiddleware::class,
],
];
```
### Manual Exception Creation
```php
use Jqhtml\LaravelBridge\JqhtmlException;
// Create from JavaScript error data
$jsError = json_decode($errorJson, true);
$exception = JqhtmlException::createFromJsError($jsError);
// Create manually
$exception = new JqhtmlException(
'Unclosed component definition',
'templates/user-card.jqhtml', // template file
42, // line number
15, // column number
$sourceCode, // template source
'Did you forget </Define:UserCard>?' // suggestion
);
```
### Integration with Node.js Compiler
When compiling JQHTML templates with Node.js, catch errors and pass them to Laravel:
```javascript
// Node.js side
try {
const compiled = jqhtml.compile(template);
} catch (error) {
// Send error details to Laravel
const errorData = {
message: error.message,
filename: error.filename,
line: error.line,
column: error.column,
source: error.source,
suggestion: error.suggestion
};
// Pass to Laravel via API or process communication
sendToLaravel(errorData);
}
```
```php
// Laravel side - Best Practice
use Jqhtml\LaravelBridge\JqhtmlException;
// Parse compiler JSON output
$errorData = json_decode($compiler_output, true);
if ($errorData && isset($errorData['error'])) {
// JqhtmlException extends ViewException, so this will show
// the template file as the error source in Laravel/Ignition
$exception = JqhtmlException::createFromJsError(
$errorData['error'],
$templatePath // Pass the actual template path
);
throw $exception;
}
// Alternative: Direct ViewException usage (if not using the bridge)
if ($errorData && isset($errorData['error'])) {
$error = $errorData['error'];
$line = $error['line'] ?? 1;
throw new \Illuminate\View\ViewException(
$error['message'],
0, // code
1, // severity
$templatePath, // file
$line, // line
null // previous
);
}
```
### Source Map Support
The package supports source maps for mapping compiled JavaScript back to original JQHTML templates:
```php
// Configure source map storage location
config(['jqhtml.source_maps_path' => storage_path('sourcemaps')]);
// The formatter will automatically look for source maps
// when an error includes a compiled file reference
$exception->setCompiledFile('/path/to/compiled.js');
```
### Custom Error Views
Create a custom error view at `resources/views/jqhtml/error.blade.php`:
```blade
@extends('errors::layout')
@section('title', 'JQHTML Template Error')
@section('message')
<div class="jqhtml-error">
<h2>{{ $exception->getMessage() }}</h2>
@if($exception->getSuggestion())
<div class="suggestion">
💡 {{ $exception->getSuggestion() }}
</div>
@endif
@if($exception->getTemplateFile())
<div class="location">
📍 {{ $exception->getTemplateFile() }}:{{ $exception->getTemplateLine() }}:{{ $exception->getTemplateColumn() }}
</div>
@endif
@if(isset($error_data['source_context']))
<div class="source-context">
<pre><code>@foreach($error_data['source_context'] as $line)
@if($line['is_error_line'])<strong>> {{ $line['line_number'] }} | {{ $line['content'] }}
@if($line['error_column']){{ str_repeat(' ', $line['error_column'] + 8) }}{{ str_repeat('^', 20) }}@endif</strong>
@else {{ $line['line_number'] }} | {{ $line['content'] }}
@endif
@endforeach</code></pre>
</div>
@endif
</div>
@endsection
```
## Configuration Options
```php
return [
// Where to store source map files
'source_maps_path' => storage_path('jqhtml-sourcemaps'),
// Show source code context in errors
'show_source_context' => env('APP_DEBUG', false),
// Lines of context to show
'context_lines' => 5,
// Cache compiled templates
'cache_compiled' => !env('APP_DEBUG', false),
// Where to store compiled templates
'compiled_path' => storage_path('jqhtml-compiled'),
// Enable source map generation
'enable_source_maps' => env('APP_DEBUG', false),
// Source map mode: 'inline', 'external', or 'both'
'source_map_mode' => 'external',
];
```
## Laravel Ignition Integration
The package automatically integrates with Laravel Ignition (if installed) to provide enhanced error display:
- Template file location with line and column numbers
- Source code context with error highlighting
- Helpful suggestions for common mistakes
- Source map resolution for compiled files
## API Reference
### JqhtmlException
Main exception class for JQHTML template errors. **Extends Laravel's `ViewException`** for optimal integration with Laravel's error handling and Ignition error pages.
When thrown, this exception ensures:
- The template file appears as the error source in Laravel/Ignition
- Line numbers point to the actual template location
- The error page shows your JQHTML template, not the PHP processor
### JqhtmlErrorFormatter
Formats exceptions for display with source context and source map support.
### JqhtmlExceptionRenderer
Renders exceptions for web and JSON responses.
### JqhtmlServiceProvider
Registers services and extends Laravel's exception handler.
### JqhtmlErrorMiddleware
Middleware for catching and wrapping JQHTML errors.
## License
MIT

51
node_modules/@jqhtml/core/laravel-bridge/autoload.php generated vendored Executable file
View File

@@ -0,0 +1,51 @@
<?php
/**
* JQHTML Laravel Bridge Autoloader
*
* Include this file from your Laravel project to load the JQHTML error handling bridge:
*
* require_once base_path('node_modules/@jqhtml/core/laravel-bridge/autoload.php');
*
* Or if using in a service provider:
*
* require_once dirname(__DIR__, 3) . '/node_modules/@jqhtml/core/laravel-bridge/autoload.php';
*/
// This is a Laravel bridge - it REQUIRES Laravel to function
if (!class_exists('Illuminate\\Support\\ServiceProvider')) {
throw new \RuntimeException(
'JQHTML Laravel Bridge requires Laravel. ' .
'This file should only be included from within a Laravel application.'
);
}
// Register the autoloader for JQHTML Laravel Bridge classes
spl_autoload_register(function ($class) {
// Check if the class is in the Jqhtml\LaravelBridge namespace
$prefix = 'Jqhtml\\LaravelBridge\\';
$len = strlen($prefix);
if (strncmp($prefix, $class, $len) !== 0) {
return;
}
// Get the relative class name
$relative_class = substr($class, $len);
// Replace namespace separators with directory separators
$file = __DIR__ . '/src/' . str_replace('\\', '/', $relative_class) . '.php';
// If the file exists, require it
if (file_exists($file)) {
require $file;
}
});
// Auto-register the service provider if in Laravel application context
if (function_exists('app') && app() instanceof \Illuminate\Foundation\Application) {
app()->register(\Jqhtml\LaravelBridge\JqhtmlServiceProvider::class);
}
// Return the namespace for convenience
return 'Jqhtml\\LaravelBridge';

34
node_modules/@jqhtml/core/laravel-bridge/composer.json generated vendored Executable file
View File

@@ -0,0 +1,34 @@
{
"name": "jqhtml/laravel-bridge",
"description": "Laravel integration bridge for JQHTML template error reporting and source maps",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "JQHTML Team"
}
],
"require": {
"php": "^7.4|^8.0",
"illuminate/support": "^8.0|^9.0|^10.0|^11.0"
},
"autoload": {
"psr-4": {
"Jqhtml\\LaravelBridge\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Jqhtml\\LaravelBridge\\Tests\\": "tests/"
}
},
"extra": {
"laravel": {
"providers": [
"Jqhtml\\LaravelBridge\\JqhtmlServiceProvider"
]
}
},
"minimum-stability": "stable",
"prefer-stable": true
}

82
node_modules/@jqhtml/core/laravel-bridge/config/jqhtml.php generated vendored Executable file
View File

@@ -0,0 +1,82 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| JQHTML Source Maps Path
|--------------------------------------------------------------------------
|
| This option defines where JQHTML source map files should be stored.
| Source maps help map compiled JavaScript back to original JQHTML templates
| for better error reporting and debugging.
|
*/
'source_maps_path' => storage_path('jqhtml-sourcemaps'),
/*
|--------------------------------------------------------------------------
| Show Source Context
|--------------------------------------------------------------------------
|
| When enabled, error messages will include the surrounding source code
| context to help identify the exact location and nature of the error.
|
*/
'show_source_context' => env('APP_DEBUG', false),
/*
|--------------------------------------------------------------------------
| Error Context Lines
|--------------------------------------------------------------------------
|
| Number of lines to show before and after the error line when displaying
| source context in error messages.
|
*/
'context_lines' => 5,
/*
|--------------------------------------------------------------------------
| Cache Compiled Templates
|--------------------------------------------------------------------------
|
| When enabled, compiled JQHTML templates will be cached to improve
| performance. Disable during development for immediate template updates.
|
*/
'cache_compiled' => env('JQHTML_CACHE', !env('APP_DEBUG', false)),
/*
|--------------------------------------------------------------------------
| Compiled Templates Path
|--------------------------------------------------------------------------
|
| Directory where compiled JQHTML templates should be stored.
|
*/
'compiled_path' => storage_path('jqhtml-compiled'),
/*
|--------------------------------------------------------------------------
| Enable Source Maps
|--------------------------------------------------------------------------
|
| Whether to generate source maps for compiled templates. Source maps
| increase compilation time slightly but provide better debugging.
|
*/
'enable_source_maps' => env('APP_DEBUG', false),
/*
|--------------------------------------------------------------------------
| Source Map Mode
|--------------------------------------------------------------------------
|
| How source maps should be generated:
| - 'inline': Embed source map directly in compiled file
| - 'external': Save source map as separate .map file
| - 'both': Generate both inline and external source maps
|
*/
'source_map_mode' => 'external',
];

View File

@@ -0,0 +1,201 @@
#!/usr/bin/env node
/**
* Example Node.js integration for sending JQHTML errors to Laravel
*
* This shows how to compile JQHTML templates and send any errors
* to a Laravel backend for proper error handling and display.
*/
import { Lexer, Parser, CodeGenerator, JQHTMLParseError } from '@jqhtml/parser';
import fs from 'fs';
import fetch from 'node-fetch'; // or axios, etc.
/**
* Compile a JQHTML template and handle errors
*/
async function compileTemplate(templatePath, laravelEndpoint) {
try {
// Read template file
const source = fs.readFileSync(templatePath, 'utf8');
const filename = templatePath;
// Compile with source maps
const lexer = new Lexer(source);
const tokens = lexer.tokenize();
const parser = new Parser(tokens, source, filename);
const ast = parser.parse();
const generator = new CodeGenerator();
const result = generator.generateWithSourceMap(ast, filename, source);
// Success - return compiled code
return {
success: true,
code: result.code,
sourceMap: result.map
};
} catch (error) {
// Format error for Laravel
const errorData = formatErrorForLaravel(error, templatePath);
// Send to Laravel if endpoint provided
if (laravelEndpoint) {
await sendErrorToLaravel(errorData, laravelEndpoint);
}
// Return error response
return {
success: false,
error: errorData
};
}
}
/**
* Format a JavaScript error for Laravel consumption
*/
function formatErrorForLaravel(error, templatePath) {
// Check if it's a JQHTML parse error with full details
if (error instanceof JQHTMLParseError || error.name === 'JQHTMLParseError') {
return {
message: error.message,
filename: error.filename || templatePath,
line: error.line,
column: error.column,
source: error.source,
suggestion: error.suggestion,
severity: error.severity || 'error',
endLine: error.endLine,
endColumn: error.endColumn
};
}
// Generic error - try to extract what we can
const errorData = {
message: error.message || String(error),
filename: templatePath,
severity: 'error'
};
// Try to parse location from error message
const locationMatch = error.message.match(/at line (\d+), column (\d+)/);
if (locationMatch) {
errorData.line = parseInt(locationMatch[1]);
errorData.column = parseInt(locationMatch[2]);
}
return errorData;
}
/**
* Send error data to Laravel backend
*/
async function sendErrorToLaravel(errorData, endpoint) {
try {
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
error: errorData,
context: {
node_version: process.version,
timestamp: new Date().toISOString(),
environment: process.env.NODE_ENV || 'development'
}
})
});
if (!response.ok) {
console.error('Failed to send error to Laravel:', response.statusText);
}
return response.json();
} catch (err) {
console.error('Error communicating with Laravel:', err);
}
}
/**
* Express/HTTP endpoint for template compilation
*
* This could be used in a Node.js service that compiles templates
* for a Laravel application.
*/
export function createCompilationEndpoint(app, laravelErrorEndpoint) {
app.post('/compile-jqhtml', async (req, res) => {
const { template, filename, source } = req.body;
try {
// Compile template
const lexer = new Lexer(source || template);
const tokens = lexer.tokenize();
const parser = new Parser(tokens, source || template, filename);
const ast = parser.parse();
const generator = new CodeGenerator();
const result = generator.generateWithSourceMap(
ast,
filename || 'template.jqhtml',
source || template
);
// Success response
res.json({
success: true,
compiled: {
code: result.code,
map: result.map
}
});
} catch (error) {
// Format error for Laravel
const errorData = formatErrorForLaravel(error, filename);
// Send error to Laravel for logging/display
if (laravelErrorEndpoint) {
sendErrorToLaravel(errorData, laravelErrorEndpoint);
}
// Return error response
res.status(400).json({
success: false,
error: errorData
});
}
});
}
/**
* CLI usage example
*/
if (import.meta.url === `file://${process.argv[1]}`) {
const templatePath = process.argv[2];
const laravelEndpoint = process.argv[3] || process.env.LARAVEL_ERROR_ENDPOINT;
if (!templatePath) {
console.error('Usage: node-integration.js <template-file> [laravel-endpoint]');
process.exit(1);
}
compileTemplate(templatePath, laravelEndpoint).then(result => {
if (result.success) {
console.log('Compilation successful!');
console.log('Code length:', result.code.length);
console.log('Source map:', result.sourceMap ? 'Generated' : 'Not generated');
} else {
console.error('Compilation failed!');
console.error(result.error);
process.exit(1);
}
});
}
// Export for use as module
export { compileTemplate, formatErrorForLaravel, sendErrorToLaravel };

View File

@@ -0,0 +1,187 @@
<?php
namespace Jqhtml\LaravelBridge;
use Throwable;
class JqhtmlErrorFormatter
{
protected $sourceMapPath;
protected $showSourceContext;
protected $sourceMapCache = [];
public function __construct(?string $sourceMapPath = null, bool $showSourceContext = true)
{
$this->sourceMapPath = $sourceMapPath;
$this->showSourceContext = $showSourceContext;
}
/**
* Format a JQHTML exception for Laravel's error handler
*/
public function format(JqhtmlException $exception): array
{
$data = [
'message' => $exception->getMessage(),
'exception' => get_class($exception),
'file' => $exception->getTemplateFile() ?? $exception->getFile(),
'line' => $exception->getTemplateLine() ?? $exception->getLine(),
'column' => $exception->getTemplateColumn(),
'error_type' => $exception->getErrorType(),
];
if ($exception->getSuggestion()) {
$data['suggestion'] = $exception->getSuggestion();
}
if ($this->showSourceContext && $exception->getSourceCode()) {
$data['source_context'] = $this->getSourceContext($exception);
}
// Try to resolve source map if we have a compiled file location
if ($exception->getCompiledFile() && $this->sourceMapPath) {
$data['source_map'] = $this->resolveSourceMap($exception);
}
return $data;
}
/**
* Format for JSON responses
*/
public function formatForJson(JqhtmlException $exception): array
{
return [
'error' => true,
'type' => 'jqhtml_error',
'message' => $exception->getMessage(),
'details' => $this->format($exception),
];
}
/**
* Get source code context for display
*/
protected function getSourceContext(JqhtmlException $exception): array
{
$source = $exception->getSourceCode();
$line = $exception->getTemplateLine();
$column = $exception->getTemplateColumn();
if (!$source || !$line) {
return [];
}
$lines = explode("\n", $source);
$lineIndex = $line - 1;
// Get 5 lines before and after for context
$contextSize = 5;
$start = max(0, $lineIndex - $contextSize);
$end = min(count($lines) - 1, $lineIndex + $contextSize);
$context = [];
for ($i = $start; $i <= $end; $i++) {
$context[] = [
'line_number' => $i + 1,
'content' => $lines[$i],
'is_error_line' => $i === $lineIndex,
'error_column' => $i === $lineIndex ? $column : null,
];
}
return $context;
}
/**
* Attempt to resolve source map for better error location
*/
protected function resolveSourceMap(JqhtmlException $exception): ?array
{
$compiledFile = $exception->getCompiledFile();
if (!$compiledFile || !file_exists($compiledFile)) {
return null;
}
// Look for source map file
$mapFile = $compiledFile . '.map';
if (!file_exists($mapFile)) {
// Try in the configured source map directory
if ($this->sourceMapPath) {
$mapFile = $this->sourceMapPath . '/' . basename($compiledFile) . '.map';
if (!file_exists($mapFile)) {
return null;
}
} else {
return null;
}
}
// Load and parse source map
if (!isset($this->sourceMapCache[$mapFile])) {
$mapContent = file_get_contents($mapFile);
$this->sourceMapCache[$mapFile] = json_decode($mapContent, true);
}
$sourceMap = $this->sourceMapCache[$mapFile];
if (!$sourceMap) {
return null;
}
return [
'version' => $sourceMap['version'] ?? null,
'sources' => $sourceMap['sources'] ?? [],
'file' => $sourceMap['file'] ?? null,
'has_mappings' => !empty($sourceMap['mappings']),
];
}
/**
* Convert a generic exception to JQHTML exception if it contains JQHTML error data
*/
public function wrapException(Throwable $exception): Throwable
{
// Check if the exception message contains JQHTML error data
$message = $exception->getMessage();
// Look for JQHTML error patterns
if (strpos($message, 'JQHTMLParseError') !== false ||
strpos($message, 'at line') !== false && strpos($message, 'column') !== false) {
// Try to extract error details from the message
$templateFile = null;
$templateLine = null;
$templateColumn = null;
// Extract location info from "at filename:line:column" format
if (preg_match('/at\s+([^:]+):(\d+):(\d+)/', $message, $matches)) {
$templateFile = $matches[1];
$templateLine = (int)$matches[2];
$templateColumn = (int)$matches[3];
} elseif (preg_match('/at\s+line\s+(\d+),\s+column\s+(\d+)/', $message, $matches)) {
$templateLine = (int)$matches[1];
$templateColumn = (int)$matches[2];
}
// Extract suggestion if present
$suggestion = null;
if (preg_match('/Did you\s+(.+?)\?/', $message, $matches)) {
$suggestion = 'Did you ' . $matches[1] . '?';
}
return new JqhtmlException(
$message,
$templateFile,
$templateLine,
$templateColumn,
null,
$suggestion,
'parse',
$exception->getCode(),
$exception
);
}
return $exception;
}
}

View File

@@ -0,0 +1,173 @@
<?php
namespace Jqhtml\LaravelBridge;
use Illuminate\View\ViewException;
use Throwable;
class JqhtmlException extends ViewException
{
protected $templateFile;
protected $templateLine;
protected $templateColumn;
protected $sourceCode;
protected $suggestion;
protected $errorType;
protected $compiledFile;
public function __construct(
string $message,
?string $templateFile = null,
?int $templateLine = null,
?int $templateColumn = null,
?string $sourceCode = null,
?string $suggestion = null,
string $errorType = 'parse',
int $code = 0,
?Throwable $previous = null
) {
// Call ViewException constructor with template file and line info
// ViewException signature: __construct($message, $code = 0, $severity = 1, $filename = '', $lineno = 0, $previous = null)
parent::__construct(
$message,
$code,
1, // severity
$templateFile ?? '',
$templateLine ?? 0,
$previous
);
$this->templateFile = $templateFile;
$this->templateLine = $templateLine;
$this->templateColumn = $templateColumn;
$this->sourceCode = $sourceCode;
$this->suggestion = $suggestion;
$this->errorType = $errorType;
}
/**
* Create from a JavaScript error object or JSON string
*/
public static function createFromJsError($jsError, ?string $compiledFile = null): self
{
if (is_string($jsError)) {
$jsError = json_decode($jsError, true);
}
return new self(
$jsError['message'] ?? 'Unknown JQHTML error',
$jsError['filename'] ?? $jsError['templateFile'] ?? null,
$jsError['line'] ?? null,
$jsError['column'] ?? null,
$jsError['source'] ?? null,
$jsError['suggestion'] ?? null,
$jsError['severity'] ?? 'error'
);
}
public function getTemplateFile(): ?string
{
return $this->templateFile;
}
public function getTemplateLine(): ?int
{
return $this->templateLine;
}
public function getTemplateColumn(): ?int
{
return $this->templateColumn;
}
public function getSourceCode(): ?string
{
return $this->sourceCode;
}
public function getSuggestion(): ?string
{
return $this->suggestion;
}
public function getErrorType(): string
{
return $this->errorType;
}
public function getCompiledFile(): ?string
{
return $this->compiledFile;
}
public function setCompiledFile(string $file): self
{
$this->compiledFile = $file;
return $this;
}
/**
* Get formatted error message with context
*/
public function getFormattedMessage(): string
{
$message = $this->getMessage();
if ($this->suggestion) {
$message .= "\n" . $this->suggestion;
}
if ($this->templateFile) {
$message .= sprintf(
"\n at %s:%d:%d",
$this->templateFile,
$this->templateLine ?? 0,
$this->templateColumn ?? 0
);
}
if ($this->sourceCode && $this->templateLine) {
$message .= "\n\n" . $this->getCodeSnippet();
}
return $message;
}
/**
* Get code snippet with error highlighting
*/
protected function getCodeSnippet(): string
{
if (!$this->sourceCode || !$this->templateLine) {
return '';
}
$lines = explode("\n", $this->sourceCode);
$lineIndex = $this->templateLine - 1;
// Show 3 lines before and after for context
$contextLines = 3;
$startLine = max(0, $lineIndex - $contextLines);
$endLine = min(count($lines) - 1, $lineIndex + $contextLines);
$snippet = '';
for ($i = $startLine; $i <= $endLine; $i++) {
$lineNum = $i + 1;
$isErrorLine = $i === $lineIndex;
$prefix = $isErrorLine ? '>' : ' ';
// Line number with padding
$lineNumStr = str_pad((string)$lineNum, 5, ' ', STR_PAD_LEFT);
$snippet .= sprintf("%s %s | %s\n", $prefix, $lineNumStr, $lines[$i]);
// Add pointer to error column
if ($isErrorLine && $this->templateColumn) {
$spaces = str_repeat(' ', $this->templateColumn + 8);
$carets = str_repeat('^', min(strlen($lines[$i]) - $this->templateColumn + 1, 20));
$snippet .= $spaces . $carets . "\n";
}
}
return $snippet;
}
}

View File

@@ -0,0 +1,93 @@
<?php
namespace Jqhtml\LaravelBridge;
use Illuminate\Contracts\View\View;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Throwable;
class JqhtmlExceptionRenderer
{
protected $formatter;
public function __construct(JqhtmlErrorFormatter $formatter)
{
$this->formatter = $formatter;
}
/**
* Render a JQHTML exception for display
*/
public function render(Request $request, JqhtmlException $exception): ?Response
{
if ($request->expectsJson()) {
return $this->renderJson($exception);
}
// For development, enhance the error page with JQHTML-specific info
if (app()->hasDebugModeEnabled()) {
return $this->renderDebugView($exception);
}
// In production, return null to let Laravel handle it normally
return null;
}
/**
* Render JSON error response
*/
protected function renderJson(JqhtmlException $exception): JsonResponse
{
return response()->json(
$this->formatter->formatForJson($exception),
500
);
}
/**
* Render debug view with enhanced JQHTML error information
*/
protected function renderDebugView(JqhtmlException $exception): ?Response
{
// Get formatted error data
$errorData = $this->formatter->format($exception);
// If using Laravel's Ignition error page, enhance it with our data
if (class_exists(\Spatie\LaravelIgnition\Facades\Flare::class)) {
\Spatie\LaravelIgnition\Facades\Flare::context('JQHTML Error', $errorData);
return null; // Let Ignition handle the rendering
}
// If using older Laravel or custom error handling
try {
// Check if we have a custom view
if (view()->exists('jqhtml::error')) {
return response()->view('jqhtml::error', [
'exception' => $exception,
'error_data' => $errorData,
], 500);
}
} catch (Throwable $e) {
// Fall back to letting Laravel handle it
}
return null;
}
/**
* Check if an exception should be handled by this renderer
*/
public function shouldHandle(Throwable $exception): bool
{
if ($exception instanceof JqhtmlException) {
return true;
}
// Check if it's a wrapped JQHTML error
$message = $exception->getMessage();
return strpos($message, 'JQHTMLParseError') !== false ||
strpos($message, 'JQHTML') !== false;
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace Jqhtml\LaravelBridge;
use Illuminate\Support\ServiceProvider;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
class JqhtmlServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register()
{
// Register the error formatter as a singleton
$this->app->singleton(JqhtmlErrorFormatter::class, function ($app) {
return new JqhtmlErrorFormatter(
$app['config']->get('jqhtml.source_maps_path', storage_path('jqhtml-sourcemaps')),
$app['config']->get('jqhtml.show_source_context', true)
);
});
// Register the exception renderer
$this->app->singleton(JqhtmlExceptionRenderer::class, function ($app) {
return new JqhtmlExceptionRenderer(
$app->make(JqhtmlErrorFormatter::class)
);
});
}
/**
* Bootstrap any application services.
*/
public function boot()
{
// Publish configuration
$this->publishes([
__DIR__ . '/../config/jqhtml.php' => config_path('jqhtml.php'),
], 'jqhtml-config');
// Extend Laravel's exception handler
$this->extendExceptionHandler();
}
/**
* Extend Laravel's exception handler to handle JQHTML exceptions
*/
protected function extendExceptionHandler()
{
$this->app->extend(ExceptionHandler::class, function ($handler, $app) {
// Hook into the exception rendering process
$handler->reportable(function (JqhtmlException $e) {
// Custom reporting logic if needed
});
$handler->renderable(function (JqhtmlException $e, $request) use ($app) {
if ($request->expectsJson()) {
return response()->json(
$app->make(JqhtmlErrorFormatter::class)->formatForJson($e),
500
);
}
// For web requests, let Laravel's default HTML handler display it
// with our enhanced error information
return null;
});
return $handler;
});
}
}

View File

@@ -0,0 +1,90 @@
<?php
namespace Jqhtml\LaravelBridge\Middleware;
use Closure;
use Illuminate\Http\Request;
use Jqhtml\LaravelBridge\JqhtmlErrorFormatter;
use Jqhtml\LaravelBridge\JqhtmlException;
use Throwable;
class JqhtmlErrorMiddleware
{
protected $formatter;
public function __construct(JqhtmlErrorFormatter $formatter)
{
$this->formatter = $formatter;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
try {
return $next($request);
} catch (Throwable $exception) {
// Check if this might be a JQHTML error
if ($this->isJqhtmlError($exception)) {
// Wrap it in our exception type for better handling
$wrapped = $this->formatter->wrapException($exception);
if ($wrapped instanceof JqhtmlException) {
// Add request context
$wrapped->setCompiledFile(
$this->getCompiledFileFromRequest($request)
);
throw $wrapped;
}
}
// Not a JQHTML error, rethrow as-is
throw $exception;
}
}
/**
* Check if an exception appears to be JQHTML-related
*/
protected function isJqhtmlError(Throwable $exception): bool
{
$message = $exception->getMessage();
return strpos($message, 'JQHTML') !== false ||
strpos($message, 'jqhtml') !== false ||
strpos($message, 'at line') !== false && strpos($message, 'column') !== false ||
strpos($message, 'Unclosed component') !== false ||
strpos($message, 'Mismatched tags') !== false;
}
/**
* Try to determine the compiled file from the request
*/
protected function getCompiledFileFromRequest(Request $request): ?string
{
// Check if the request has a reference to a compiled template
$route = $request->route();
if ($route && method_exists($route, 'getAction')) {
$action = $route->getAction();
// Look for JQHTML template reference in route action
if (isset($action['jqhtml_template'])) {
return $action['jqhtml_template'];
}
}
// Check request attributes
if ($request->has('_jqhtml_compiled')) {
return $request->get('_jqhtml_compiled');
}
return null;
}
}

View File

@@ -0,0 +1,219 @@
<?php
namespace Jqhtml\LaravelBridge\Tests;
use PHPUnit\Framework\TestCase;
use Jqhtml\LaravelBridge\JqhtmlException;
use Jqhtml\LaravelBridge\JqhtmlErrorFormatter;
class ExceptionFormattingTest extends TestCase
{
protected $formatter;
protected function setUp(): void
{
parent::setUp();
$this->formatter = new JqhtmlErrorFormatter();
}
public function testBasicExceptionCreation()
{
$exception = new JqhtmlException(
'Unclosed component definition',
'templates/test.jqhtml',
10,
15
);
$this->assertEquals('Unclosed component definition', $exception->getMessage());
$this->assertEquals('templates/test.jqhtml', $exception->getTemplateFile());
$this->assertEquals(10, $exception->getTemplateLine());
$this->assertEquals(15, $exception->getTemplateColumn());
}
public function testExceptionWithSuggestion()
{
$exception = new JqhtmlException(
'Unclosed component definition',
'templates/test.jqhtml',
10,
15,
null,
'Did you forget </Define:ComponentName>?'
);
$this->assertEquals('Did you forget </Define:ComponentName>?', $exception->getSuggestion());
$formatted = $exception->getFormattedMessage();
$this->assertStringContainsString('Did you forget', $formatted);
}
public function testExceptionFromJsError()
{
$jsError = [
'message' => 'Syntax error: unexpected token',
'filename' => 'app.jqhtml',
'line' => 42,
'column' => 8,
'suggestion' => 'Check for missing closing tags',
'severity' => 'error'
];
$exception = JqhtmlException::createFromJsError($jsError);
$this->assertEquals('Syntax error: unexpected token', $exception->getMessage());
$this->assertEquals('app.jqhtml', $exception->getTemplateFile());
$this->assertEquals(42, $exception->getTemplateLine());
$this->assertEquals(8, $exception->getTemplateColumn());
$this->assertEquals('Check for missing closing tags', $exception->getSuggestion());
}
public function testExceptionFromJsonString()
{
$json = json_encode([
'message' => 'Parse error',
'templateFile' => 'template.jqhtml',
'line' => 5,
'column' => 10
]);
$exception = JqhtmlException::createFromJsError($json);
$this->assertEquals('Parse error', $exception->getMessage());
$this->assertEquals('template.jqhtml', $exception->getTemplateFile());
$this->assertEquals(5, $exception->getTemplateLine());
$this->assertEquals(10, $exception->getTemplateColumn());
}
public function testFormatterBasicFormat()
{
$exception = new JqhtmlException(
'Test error',
'test.jqhtml',
20,
5
);
$formatted = $this->formatter->format($exception);
$this->assertArrayHasKey('message', $formatted);
$this->assertArrayHasKey('file', $formatted);
$this->assertArrayHasKey('line', $formatted);
$this->assertArrayHasKey('column', $formatted);
$this->assertArrayHasKey('error_type', $formatted);
$this->assertEquals('Test error', $formatted['message']);
$this->assertEquals('test.jqhtml', $formatted['file']);
$this->assertEquals(20, $formatted['line']);
$this->assertEquals(5, $formatted['column']);
}
public function testFormatterWithSourceContext()
{
$sourceCode = "line 1\nline 2\nline 3 with error\nline 4\nline 5";
$exception = new JqhtmlException(
'Error on line 3',
'test.jqhtml',
3,
10,
$sourceCode
);
$formatter = new JqhtmlErrorFormatter(null, true);
$formatted = $formatter->format($exception);
$this->assertArrayHasKey('source_context', $formatted);
$context = $formatted['source_context'];
$this->assertIsArray($context);
// Find the error line in context
$errorLine = null;
foreach ($context as $line) {
if ($line['is_error_line']) {
$errorLine = $line;
break;
}
}
$this->assertNotNull($errorLine);
$this->assertEquals(3, $errorLine['line_number']);
$this->assertEquals('line 3 with error', $errorLine['content']);
$this->assertEquals(10, $errorLine['error_column']);
}
public function testFormatterJsonFormat()
{
$exception = new JqhtmlException('JSON test error');
$json = $this->formatter->formatForJson($exception);
$this->assertArrayHasKey('error', $json);
$this->assertArrayHasKey('type', $json);
$this->assertArrayHasKey('message', $json);
$this->assertArrayHasKey('details', $json);
$this->assertTrue($json['error']);
$this->assertEquals('jqhtml_error', $json['type']);
$this->assertEquals('JSON test error', $json['message']);
}
public function testWrapGenericException()
{
$genericException = new \Exception(
'JQHTMLParseError: Unclosed tag at line 10, column 5'
);
$wrapped = $this->formatter->wrapException($genericException);
$this->assertInstanceOf(JqhtmlException::class, $wrapped);
$this->assertEquals(10, $wrapped->getTemplateLine());
$this->assertEquals(5, $wrapped->getTemplateColumn());
}
public function testWrapExceptionWithFilename()
{
$genericException = new \Exception(
'Error at component.jqhtml:15:20 - syntax error'
);
$wrapped = $this->formatter->wrapException($genericException);
$this->assertInstanceOf(JqhtmlException::class, $wrapped);
$this->assertEquals('component.jqhtml', $wrapped->getTemplateFile());
$this->assertEquals(15, $wrapped->getTemplateLine());
$this->assertEquals(20, $wrapped->getTemplateColumn());
}
public function testCodeSnippetGeneration()
{
$source = implode("\n", [
'line 1',
'line 2',
'line 3',
'error is here', // line 4
'line 5',
'line 6',
'line 7'
]);
$exception = new JqhtmlException(
'Error message',
'test.jqhtml',
4,
8,
$source
);
$formatted = $exception->getFormattedMessage();
// Should show context lines
$this->assertStringContainsString('line 3', $formatted);
$this->assertStringContainsString('error is here', $formatted);
$this->assertStringContainsString('line 5', $formatted);
// Should have error pointer
$this->assertStringContainsString('^', $formatted);
}
}

76
node_modules/@jqhtml/core/package.json generated vendored Executable file
View File

@@ -0,0 +1,76 @@
{
"name": "@jqhtml/core",
"version": "2.2.137",
"description": "Core runtime library for JQHTML",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs",
"types": "./dist/index.d.ts"
},
"./debug": {
"import": "./dist/jqhtml-debug.esm.js",
"types": "./dist/debug.d.ts"
},
"./bundle": {
"import": "./dist/jqhtml-core.esm.js"
}
},
"scripts": {
"build": "rollup -c",
"build:tsc": "tsc",
"watch": "rollup -c -w",
"test": "jest",
"clean": "rm -rf dist"
},
"peerDependencies": {
"jquery": "^3.7.0"
},
"browser": true,
"publishConfig": {
"access": "public",
"registry": "https://privatenpm.hanson.xyz/"
},
"files": [
"dist",
"laravel-bridge",
"README.md",
"LLM_REFERENCE.md",
"LICENSE"
],
"keywords": [
"jquery",
"components",
"framework",
"jqhtml",
"templating"
],
"author": "JQHTML Team",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/jqhtml/jqhtml.git",
"directory": "packages/core"
},
"dependencies": {
"@rollup/plugin-node-resolve": "^16.0.1",
"@rollup/plugin-replace": "^6.0.2",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^12.1.4",
"@types/jest": "^29.5.14",
"@types/jquery": "^3.5.32",
"esbuild": "^0.25.9",
"jest": "^29.5.0",
"jsdom": "^26.1.0",
"playwright": "^1.53.2",
"rollup": "^4.49.0",
"rollup-plugin-dts": "^6.2.3",
"ts-jest": "^29.1.0",
"tslib": "^2.8.1",
"typescript": "^5.9.2"
},
"devDependencies": null
}

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"
}
}

21
node_modules/@jqhtml/router/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.

355
node_modules/@jqhtml/router/LLM_REFERENCE.md generated vendored Executable file
View File

@@ -0,0 +1,355 @@
# JQHTML SPA Router System - LLM Reference
## Core Architecture
- Static router class `Jqhtml_Router` manages all navigation
- Routes are ES6 classes extending `Jqhtml_Route`
- Layouts are ES6 classes extending `Jqhtml_Layout`
- SPA container is ES6 class extending `Jqhtml_SPA`
- Hash routing (`#/path`) automatically enabled for `file://` protocol
- Standard routing for `http://` and `https://` protocols
## Route Definition
### Basic Route Class
```javascript
class HomeRoute extends Jqhtml_Route {
static routes = ['/']; // URL patterns this route handles
static layout = 'MainLayout'; // Layout to use (optional)
static meta = { name: 'home', requiresAuth: false }; // Metadata
async on_render() {
// Route component render logic
}
async pre_dispatch() {
// Return false to cancel navigation
// Return string URL to redirect
return true;
}
async post_dispatch() {
// Called after route fully loaded
}
}
```
### Route Patterns
```javascript
static routes = [
'/', // Exact match
'/users', // Static path
'/users/:id', // Parameter capture
'/posts/:id/comments', // Multiple segments
'/admin/:section/:id' // Multiple parameters
];
```
### Parameter Access
```javascript
class UserRoute extends Jqhtml_Route {
static routes = ['/users/:id'];
async on_load() {
// URL: /users/123?tab=profile
const userId = this.args.id; // '123' from URL pattern
const tab = this.args.tab; // 'profile' from query string
const hash = this.args.hash; // Fragment after #
}
}
```
## Layout System
### Layout Definition
```javascript
class MainLayout extends Jqhtml_Layout {
async on_render() {
this.$.html(`
<header>Navigation</header>
<main $id="content"></main> <!-- REQUIRED: Routes render here -->
<footer>Footer</footer>
`);
}
async on_route_change(old_route, new_route) {
// Update active navigation state
this.$find('.nav-link').removeClass('active');
this.$find(`[href="${new_route.path}"]`).addClass('active');
}
should_rerender() {
return false; // Layouts persist across route changes
}
async pre_dispatch(route_info) {
// Can intercept routing at layout level
if (route_info.meta.requiresAuth && !this.data.user) {
return '/login'; // Redirect
}
return true;
}
}
```
### Layout Persistence
- Layouts never re-render during route changes within same layout
- Layout instance persists until different layout needed
- `$id="content"` element required for route injection point
## SPA Application Container
### Basic SPA Setup
```javascript
class App extends Jqhtml_SPA {
async on_create() {
// Register all routes
HomeRoute.init();
UserRoute.init();
AdminRoute.init();
// Register layouts
Jqhtml_Router.register_layout('MainLayout', MainLayout);
Jqhtml_Router.register_layout('AdminLayout', AdminLayout);
// Set default layout
Jqhtml_Router.set_default_layout('MainLayout');
// Initialize router
await this.init_router({
default_layout: 'MainLayout'
});
}
async pre_dispatch(route_info) {
// Global route guard
// Runs before layout.pre_dispatch()
return true;
}
async post_dispatch(route_info) {
// Global post-navigation logic
// Runs after layout.post_dispatch()
}
}
// Mount application
$('#app').append(new App().$);
```
## Navigation Methods
### Programmatic Navigation
```javascript
// Navigate to URL
Jqhtml_Router.dispatch('/users/123');
// Navigate with replace (no history entry)
Jqhtml_Router.replace('/login');
// Navigate with parameters
const url = Jqhtml_Router.url('user-profile', { id: 123, tab: 'settings' });
Jqhtml_Router.dispatch(url);
// From within route component
this.dispatch({ id: 456 }); // Merges with current args
```
### Link Interception
- All `<a>` tags automatically intercepted for same-origin navigation
- External links and `target="_blank"` not intercepted
- Ctrl/Cmd+Click opens in new tab (not intercepted)
### URL Building
```javascript
// Build URL from route name
Jqhtml_Router.url('user-profile', { id: 123 }); // '/users/123'
// Build URL from pattern
Jqhtml_Router.build_url('/users/:id', { id: 123, tab: 'info' }); // '/users/123?tab=info'
// From route class
UserRoute.url({ id: 123 }); // '/users/123'
```
## Dispatch Lifecycle
### Execution Order
1. **URL Matching** - Parse URL, extract parameters
2. **App.pre_dispatch()** - Global guard, can cancel/redirect
3. **Layout Resolution** - Create/reuse layout based on route
4. **Layout.pre_dispatch()** - Layout-level guard
5. **Route Instantiation** - Create route component with args
6. **Route.pre_dispatch()** - Route-level guard
7. **Layout._render_route()** - Inject route into layout
8. **Route Lifecycle** - Standard component lifecycle
9. **Layout.on_route_change()** - Notify layout of change
10. **Route.post_dispatch()** - Route complete
11. **Layout.post_dispatch()** - Layout handling complete
12. **App.post_dispatch()** - Global post-navigation
### Guard Return Values
```javascript
async pre_dispatch(route_info) {
return true; // Continue navigation
return false; // Cancel navigation
return '/login'; // Redirect to different route
}
```
## Router State
### Access Current State
```javascript
// Static access
Jqhtml_Router.state = {
route: 'UserRoute', // Current route component name
layout: 'MainLayout', // Current layout name
url: '/users/123', // Current URL path
args: { id: '123' }, // Current parameters
hash: 'section' // Current hash fragment
};
// Get current route info
const current = Jqhtml_Router.current_route_info;
// { url, path, args, hash, meta, component_name, component_class, layout }
// From SPA class
Jqhtml_SPA.state; // Same as Jqhtml_Router.state
Jqhtml_SPA.current_route; // Current route info
```
## Hash Routing (file:// Protocol)
### Automatic Detection
```javascript
// Router automatically uses hash routing for file:// URLs
// file:///app/index.html#/users/123
// Translates to route: /users/123
// Hash routing format
window.location.href = 'file:///path/index.html#/users/123';
// Router sees: /users/123
// Regular routing format (http/https)
window.location.href = 'https://example.com/users/123';
// Router sees: /users/123
```
### Manual Hash Control
```javascript
if (Jqhtml_Router.use_hash_routing) {
// In hash mode
window.location.hash = '#/users/123';
} else {
// In regular mode
window.history.pushState({}, '', '/users/123');
}
```
## Route Metadata & Named Routes
### Named Route Pattern
```javascript
class UserProfileRoute extends Jqhtml_Route {
static routes = ['/users/:id/profile'];
static meta = {
name: 'user-profile', // Reference name
requiresAuth: true,
title: 'User Profile'
};
}
// Navigate by name
Jqhtml_Router.url('user-profile', { id: 123 });
UserProfileRoute.dispatch({ id: 123 });
```
## Advanced Patterns
### Nested Route Components
```javascript
class DashboardRoute extends Jqhtml_Route {
async on_render() {
this.$.html(`
<div class="dashboard">
<Sidebar />
<div $id="dashboard-content">
<!-- Sub-routes could render here -->
</div>
</div>
`);
}
}
```
### Route-Level Data Loading
```javascript
class UserRoute extends Jqhtml_Route {
async on_load() {
// Runs during component load phase
// Data fetching happens in parallel with siblings
const userId = this.args.id;
this.data.user = await fetch(`/api/users/${userId}`).then(r => r.json());
}
}
```
### Authentication Flow
```javascript
class AuthLayout extends Jqhtml_Layout {
async pre_dispatch(route_info) {
const token = localStorage.getItem('auth_token');
if (!token && route_info.meta.requiresAuth) {
// Store intended destination
sessionStorage.setItem('redirect_after_login', route_info.url);
return '/login'; // Redirect to login
}
return true;
}
}
class LoginRoute extends Jqhtml_Route {
async handleLogin() {
const token = await authenticate(this.data.credentials);
localStorage.setItem('auth_token', token);
// Redirect to intended destination or home
const redirect = sessionStorage.getItem('redirect_after_login') || '/';
sessionStorage.removeItem('redirect_after_login');
Jqhtml_Router.dispatch(redirect);
}
}
```
### Query String Handling
```javascript
// URL: /search?q=jqhtml&category=docs&page=2
class SearchRoute extends Jqhtml_Route {
static routes = ['/search'];
async on_load() {
const query = this.args.q; // 'jqhtml'
const category = this.args.category; // 'docs'
const page = parseInt(this.args.page) || 1; // 2
}
nextPage() {
// Navigate with updated query params
this.dispatch({
page: (parseInt(this.args.page) || 1) + 1
});
}
}
```
## Critical Invariants
1. Layouts must have element with `$id="content"` for route injection
2. Route dispatch is asynchronous and can be cancelled at multiple points
3. Layout instances persist across same-layout route changes
4. Routes are destroyed and recreated on each navigation
5. Hash routing auto-enabled for file:// protocol, cannot be disabled
6. All same-origin link clicks are intercepted unless explicitly prevented
7. Router state is global singleton, only one router instance exists
8. Route parameters are always strings, cast as needed
9. Query parameters merge with route parameters in args object
10. Guard functions execute in strict order: app → layout → route

226
node_modules/@jqhtml/router/README.md generated vendored Executable file
View File

@@ -0,0 +1,226 @@
# @jqhtml/router
Client-side routing for JQHTML single-page applications.
## Installation
```bash
npm install @jqhtml/router @jqhtml/core jquery
```
## Quick Start
```javascript
import { Jqhtml_SPA, Jqhtml_Router, Jqhtml_Layout, Jqhtml_Route } from '@jqhtml/router';
import jqhtml from '@jqhtml/core';
// Define routes
const routes = [
{ path: '/', component: HomeRoute },
{ path: '/about', component: AboutRoute },
{ path: '/users/:id', component: UserRoute }
];
// Create SPA application
class MyApp extends Jqhtml_SPA {
constructor($element) {
super($element);
// Configure router
this.router = new Jqhtml_Router(routes, {
mode: 'hash', // or 'history'
base: '/'
});
}
}
// Initialize
$(document).ready(() => {
const app = new MyApp($('#app'));
app.start();
});
```
## Core Components
### Jqhtml_SPA
Base class for single-page applications. Manages the application lifecycle and router integration.
### Jqhtml_Router
Handles URL routing and navigation. Supports both hash (`#/route`) and history (`/route`) modes.
### Jqhtml_Layout
Persistent layout components that don't re-render on route changes.
### Jqhtml_Route
Base class for route components that render based on the current URL.
## Features
- **Hash & History Modes**: Support for both `#/path` and `/path` routing
- **Nested Routes**: Organize routes hierarchically
- **Route Parameters**: Extract values from URLs like `/users/:id`
- **Query Strings**: Parse and access URL query parameters
- **Layouts**: Persistent wrapper components across routes
- **Lazy Loading**: Load route components on demand
- **Navigation Guards**: Control route access with guards
- **Programmatic Navigation**: Navigate via JavaScript
## Route Configuration
```javascript
const routes = [
{
path: '/',
component: HomeRoute,
name: 'home'
},
{
path: '/users',
component: UsersLayout,
children: [
{ path: '', component: UsersList },
{ path: ':id', component: UserDetail },
{ path: ':id/edit', component: UserEdit }
]
},
{
path: '*',
component: NotFoundRoute
}
];
```
## Navigation
```javascript
// Programmatic navigation
router.navigate('/users/123');
router.navigate({ name: 'home' });
// With query parameters
router.navigate('/search', { query: { q: 'jqhtml' } });
// Replace instead of push
router.replace('/login');
// Go back/forward
router.back();
router.forward();
```
## Route Components
```javascript
class UserRoute extends Jqhtml_Route {
async load() {
// Access route parameters
const userId = this.params.id;
// Load data
this.data.user = await fetchUser(userId);
}
render() {
return `
<div class="user-detail">
<h1>${this.data.user.name}</h1>
</div>
`;
}
}
```
## Layouts
```javascript
class MainLayout extends Jqhtml_Layout {
render() {
return `
<div class="layout">
<header>
<nav>
<a href="#/">Home</a>
<a href="#/about">About</a>
</nav>
</header>
<main>
<div class="route-outlet"></div>
</main>
<footer>© 2024</footer>
</div>
`;
}
// Layouts don't re-render on route change
should_rerender() {
return false;
}
}
```
## Navigation Guards
```javascript
router.beforeEach((to, from, next) => {
if (to.path.startsWith('/admin') && !isAuthenticated()) {
next('/login');
} else {
next();
}
});
router.afterEach((to, from) => {
// Update page title
document.title = to.meta.title || 'My App';
});
```
## URL Parameters
```javascript
// Route: /users/:id/posts/:postId
// URL: /users/123/posts/456?sort=date
class PostRoute extends Jqhtml_Route {
ready() {
console.log(this.params.id); // "123"
console.log(this.params.postId); // "456"
console.log(this.query.sort); // "date"
}
}
```
## Hash Mode (Default)
Best for static file hosting and development:
```javascript
const router = new Jqhtml_Router(routes, {
mode: 'hash'
});
// URLs will be: file:///path/index.html#/route
// Or: https://example.com/#/route
```
## History Mode
For production servers with proper configuration:
```javascript
const router = new Jqhtml_Router(routes, {
mode: 'history',
base: '/app/'
});
// URLs will be: https://example.com/app/route
```
## API Reference
See [LLM_REFERENCE.md](./LLM_REFERENCE.md) for detailed API documentation.
## License
MIT

1063
node_modules/@jqhtml/router/dist/index.cjs generated vendored Executable file

File diff suppressed because it is too large Load Diff

1
node_modules/@jqhtml/router/dist/index.cjs.map generated vendored Executable file

File diff suppressed because one or more lines are too long

9
node_modules/@jqhtml/router/dist/index.d.ts generated vendored Executable file
View File

@@ -0,0 +1,9 @@
/**
* JQHTML Router v2 - Client-side routing for JQHTML applications
*/
export { Jqhtml_Router } from './router.js';
export { Jqhtml_Route } from './route.js';
export { Jqhtml_Layout } from './layout.js';
export { Jqhtml_SPA } from './spa.js';
export type { RouteInfo, RouteMeta, RouterState, DispatchOptions, ParsedUrl, RouteDefinition, LayoutDefinition } from './types.js';
//# sourceMappingURL=index.d.ts.map

1
node_modules/@jqhtml/router/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":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAErC,YAAY,EACV,SAAS,EACT,SAAS,EACT,WAAW,EACX,eAAe,EACf,SAAS,EACT,eAAe,EACf,gBAAgB,EACjB,MAAM,YAAY,CAAA"}

1058
node_modules/@jqhtml/router/dist/index.js generated vendored Executable file

File diff suppressed because it is too large Load Diff

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

File diff suppressed because one or more lines are too long

1063
node_modules/@jqhtml/router/dist/jqhtml-router.esm.js generated vendored Executable file

File diff suppressed because it is too large Load Diff

1
node_modules/@jqhtml/router/dist/jqhtml-router.esm.js.map generated vendored Executable file

File diff suppressed because one or more lines are too long

51
node_modules/@jqhtml/router/dist/layout.d.ts generated vendored Executable file
View File

@@ -0,0 +1,51 @@
/**
* Base class for layout components in JQHTML Router v2
*/
import { Jqhtml_Component } from '@jqhtml/core';
import type { RouteInfo } from './types.js';
declare module '@jqhtml/core' {
interface Jqhtml_Component {
_is_layout?: boolean;
}
}
export declare class Jqhtml_Layout extends Jqhtml_Component {
static layout?: string;
_is_layout: boolean;
constructor(options?: any);
/**
* Called when the route changes within the same layout
* Override this to update layout state (e.g., active navigation items)
*/
on_route_change(old_route: RouteInfo | null, new_route: RouteInfo): Promise<void>;
/**
* Called before dispatching to a new route
* Can cancel navigation by returning false or redirect by returning a URL
*/
pre_dispatch(route_info: RouteInfo): Promise<boolean | string>;
/**
* Called after a route has fully loaded
* Can trigger redirects for post-load logic
*/
post_dispatch(route_info: RouteInfo): Promise<void>;
/**
* Get the content container where routes render
* Must contain an element with $id="content"
*/
$content(): JQuery;
/**
* Internal method to render a route into this layout
* Called by the router during dispatch
*/
_render_route(route_component: any): Promise<void>;
/**
* Override on_render for custom layout rendering
* By default does nothing - layouts should override this or use templates
*/
on_render(): Promise<void>;
/**
* Layouts should never re-render after initial load
* They persist across route changes
*/
should_rerender(): boolean;
}
//# sourceMappingURL=layout.d.ts.map

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

@@ -0,0 +1 @@
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../src/layout.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAI3C,OAAO,QAAQ,cAAc,CAAC;IAC5B,UAAU,gBAAgB;QACxB,UAAU,CAAC,EAAE,OAAO,CAAA;KACrB;CACF;AAED,qBAAa,aAAc,SAAQ,gBAAgB;IACjD,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IAGtB,UAAU,UAAO;gBAEL,OAAO,CAAC,EAAE,GAAG;IAIzB;;;OAGG;IACG,eAAe,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvF;;;OAGG;IACG,YAAY,CAAC,UAAU,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,GAAG,MAAM,CAAC;IAKpE;;;OAGG;IACG,aAAa,CAAC,UAAU,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzD;;;OAGG;IACH,QAAQ,IAAI,MAAM;IAQlB;;;OAGG;IACG,aAAa,CAAC,eAAe,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBxD;;;OAGG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAOhC;;;OAGG;IACH,eAAe,IAAI,OAAO;CAG3B"}

59
node_modules/@jqhtml/router/dist/route.d.ts generated vendored Executable file
View File

@@ -0,0 +1,59 @@
/**
* Base class for route components in JQHTML Router v2
*/
import { Jqhtml_Component } from '@jqhtml/core';
import type { RouteMeta } from './types.js';
declare module '@jqhtml/core' {
interface Jqhtml_Component {
_is_route?: boolean;
}
}
export declare class Jqhtml_Route extends Jqhtml_Component {
static routes: string[];
static layout: string;
static meta: RouteMeta;
_is_route: boolean;
args: Record<string, any>;
constructor(options?: any);
/**
* Called during app initialization to register routes
* This is where routes call register_route for each path they handle
*/
static init(): void;
/**
* Generate URL for this route with given parameters
* Static version for generating URLs without an instance
*/
static url(params?: Record<string, any>): string;
/**
* Generate URL for this route with current args merged with new params
* Instance method that includes current route parameters
*/
url(params?: Record<string, any>): string;
/**
* Navigate to this route with given parameters
* Static version for programmatic navigation
*/
static dispatch(params?: Record<string, any>): void;
/**
* Navigate to this route with current args merged with new params
* Instance method for navigation from within a route
*/
dispatch(params?: Record<string, any>): void;
/**
* Called before this route is activated
* Can be used for route-specific guards
*/
pre_dispatch(): Promise<boolean | string>;
/**
* Called after this route has fully loaded
* Can be used for analytics, etc.
*/
post_dispatch(): Promise<void>;
/**
* Override on_render for custom route rendering
* By default does nothing - routes should override this or use templates
*/
on_render(): Promise<void>;
}
//# sourceMappingURL=route.d.ts.map

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

@@ -0,0 +1 @@
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../src/route.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAI3C,OAAO,QAAQ,cAAc,CAAC;IAC5B,UAAU,gBAAgB;QACxB,SAAS,CAAC,EAAE,OAAO,CAAA;KACpB;CACF;AAED,qBAAa,YAAa,SAAQ,gBAAgB;IAEhD,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAK;IAC5B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAmB;IACxC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAK;IAG3B,SAAS,UAAO;IAGhB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAK;gBAElB,OAAO,CAAC,EAAE,GAAG;IASzB;;;OAGG;IACH,MAAM,CAAC,IAAI,IAAI,IAAI;IAQnB;;;OAGG;IACH,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM;IAehD;;;OAGG;IACH,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM;IAMzC;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAKnD;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAK5C;;;OAGG;IACG,YAAY,IAAI,OAAO,CAAC,OAAO,GAAG,MAAM,CAAC;IAK/C;;;OAGG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAIpC;;;OAGG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CAMjC"}

Some files were not shown because too many files have changed in this diff Show More