Implement JQHTML function cache ID system and fix bundle compilation Implement underscore prefix for system tables Fix JS syntax linter to support decorators and grant exception to Task system SPA: Update planning docs and wishlists with remaining features SPA: Document Navigation API abandonment and future enhancements Implement SPA browser integration with History API (Phase 1) Convert contacts view page to SPA action Convert clients pages to SPA actions and document conversion procedure SPA: Merge GET parameters and update documentation Implement SPA route URL generation in JavaScript and PHP Implement SPA bootstrap controller architecture Add SPA routing manual page (rsx:man spa) Add SPA routing documentation to CLAUDE.md Phase 4 Complete: Client-side SPA routing implementation Update get_routes() consumers for unified route structure Complete SPA Phase 3: PHP-side route type detection and is_spa flag Restore unified routes structure and Manifest_Query class Refactor route indexing and add SPA infrastructure Phase 3 Complete: SPA route registration in manifest Implement SPA Phase 2: Extract router code and test decorators Rename Jqhtml_Component to Component and complete SPA foundation setup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
@jqhtml/webpack-loader
Webpack loader for JQHTML templates. Import .jqhtml files directly in your JavaScript!
Installation
npm install --save-dev @jqhtml/webpack-loader webpack
npm install @jqhtml/core jquery
Quick Start
1. Configure Webpack
Add the loader to your webpack.config.js:
module.exports = {
module: {
rules: [
{
test: /\.jqhtml$/,
use: '@jqhtml/webpack-loader'
}
]
},
resolve: {
extensions: ['.js', '.jqhtml']
}
};
Or use the configuration helper:
const { addJQHTMLSupport } = require('@jqhtml/webpack-loader');
module.exports = addJQHTMLSupport({
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: __dirname + '/dist'
}
});
2. Create JQHTML Templates
Create UserCard.jqhtml:
<Define:UserCard>
<div class="user-card">
<h2 :text="name"></h2>
<p :text="email"></p>
<button @click="handleClick">Contact</button>
<% if (this.data.showBio): %>
<div class="bio" :html="bio"></div>
<% endif; %>
</div>
</Define:UserCard>
3. Import and Use
import UserCard from './UserCard.jqhtml';
import $ from 'jquery';
// Create component instance
const user = $('#user-container').component(UserCard, {
name: 'John Doe',
email: 'john@example.com'
});
// Set data
user.data = {
name: 'John Doe',
email: 'john@example.com',
showBio: true,
bio: '<p>Software developer</p>'
};
// Define methods
user.handleClick = function() {
console.log('Contact clicked!');
this.emit('contact', { user: this.data });
};
// Re-render to apply data
user.render();
Features
Multiple Components per File
<Define:Header>
<header>
<h1><%= this.data.title %></h1>
</header>
</Define:Header>
<Define:Footer>
<footer>
<p>© <%= this.data.year %></p>
</footer>
</Define:Footer>
Import all components:
import { Header, Footer } from './Layout.jqhtml';
// Default export is the first component
import DefaultHeader from './Layout.jqhtml';
Data Bindings
Use :prop syntax for reactive bindings:
:text="expression"- Text content:value="expression"- Input values:class="{active: isActive}"- Dynamic classes:style="{color: textColor}"- Dynamic styles:html="htmlContent"- HTML content (use carefully)
Event Handlers
Use @event syntax for event handling:
@click="methodName"- Call component method@change="updateValue"- Form events@submit="handleSubmit"- Form submission
Scoped IDs
Use $id for component-scoped element IDs:
<div $id="content">
<!-- Becomes id="content:abc123" where abc123 is the component's _cid -->
</div>
Access in component:
this.$id('content') // Returns jQuery element
Advanced Configuration
Loader Options
{
test: /\.jqhtml$/,
use: {
loader: '@jqhtml/webpack-loader',
options: {
sourceMap: true // Enable source maps (default: true)
}
}
}
With TypeScript
Add type declarations for .jqhtml imports:
// jqhtml.d.ts
declare module '*.jqhtml' {
import { Component } from '@jqhtml/core';
const component: typeof Component;
export default component;
// For multi-component files
export const Header: typeof Component;
export const Footer: typeof Component;
}
Production Build
The loader automatically handles production optimizations:
module.exports = {
mode: 'production',
module: {
rules: [
{
test: /\.jqhtml$/,
use: '@jqhtml/webpack-loader'
}
]
},
optimization: {
minimize: true
}
};
How It Works
- Parse - The loader uses
@jqhtml/parserto parse templates - Generate - Converts templates to ES module code
- Export - Each component becomes an ES6 class export
- Register - Components are automatically registered globally
The generated code looks like:
import { Component, render_template, register_component } from '@jqhtml/core';
export class UserCard extends Component {
static component_name = 'UserCard';
async on_render() {
const template_fn = function render() { /* ... */ };
await render_template(this, template_fn);
}
}
register_component('UserCard', UserCard);
export default UserCard;
Troubleshooting
Module Resolution
If webpack can't find .jqhtml files:
resolve: {
extensions: ['.js', '.jqhtml'],
modules: ['node_modules', 'src']
}
jQuery Not Found
Ensure jQuery is available:
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
})
]
Source Maps
For better debugging, ensure source maps are enabled:
devtool: 'source-map',
module: {
rules: [{
test: /\.jqhtml$/,
use: {
loader: '@jqhtml/webpack-loader',
options: { sourceMap: true }
}
}]
}
Examples
See the /examples directory for complete examples:
- Basic usage with vanilla JS
- TypeScript integration
- Multi-component layouts
- Complex data binding scenarios
License
MIT