Add JQHTML-EVENT-01 rule, document custom component events, update jqhtml

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-12-12 03:42:24 +00:00
parent c4ba2b743f
commit 5d5bd85e42
19 changed files with 364 additions and 79 deletions

18
node_modules/.package-lock.json generated vendored
View File

@@ -2211,9 +2211,9 @@
}
},
"node_modules/@jqhtml/core": {
"version": "2.3.16",
"resolved": "http://privatenpm.hanson.xyz/@jqhtml/core/-/core-2.3.16.tgz",
"integrity": "sha512-kAV24i6JqgmN3hJlRyv0dDgAyXmQUebp7WMwBTrtKJZJK+m59O0n4Uf5GeFEdfuxShMeWuz64Uh/BdrOCIB9uw==",
"version": "2.3.17",
"resolved": "http://privatenpm.hanson.xyz/@jqhtml/core/-/core-2.3.17.tgz",
"integrity": "sha512-v4M9kX1Z/NH5BNiMO9FrqdhYYT3zWehCYz7AVIZVfcOL0PHJskmoa2e+yUplgyexQ7ufQ31XzVefocdmSX3DHA==",
"license": "MIT",
"dependencies": {
"@rollup/plugin-node-resolve": "^16.0.1",
@@ -2237,9 +2237,9 @@
}
},
"node_modules/@jqhtml/parser": {
"version": "2.3.16",
"resolved": "http://privatenpm.hanson.xyz/@jqhtml/parser/-/parser-2.3.16.tgz",
"integrity": "sha512-9qKR+hH+y6JsaSquq2AGZDOvS1nFH3U4GUodrdCEIpw959XivAi5BEBPr6uJ5dN4beYmonMge1SF4XZnaoiaEw==",
"version": "2.3.17",
"resolved": "http://privatenpm.hanson.xyz/@jqhtml/parser/-/parser-2.3.17.tgz",
"integrity": "sha512-Jr7vKjqmL/JIyGa6ojmnG3xcvWLaprLqG5BLPnZHXH8hC1AG3ReVuNZs1P3dT6GCp6dYjQ3ly30sl+PbZ32IQQ==",
"license": "MIT",
"dependencies": {
"@types/jest": "^29.5.11",
@@ -2277,9 +2277,9 @@
}
},
"node_modules/@jqhtml/vscode-extension": {
"version": "2.3.16",
"resolved": "http://privatenpm.hanson.xyz/@jqhtml/vscode-extension/-/vscode-extension-2.3.16.tgz",
"integrity": "sha512-QS+soQLhZquTQ5V5nCo3J13ztKW8cXacNaW05vJSvaYQnKFmSvmiZrGfnaF/7UToIYfs5+tpjDRGkt72ENTQ/A==",
"version": "2.3.17",
"resolved": "http://privatenpm.hanson.xyz/@jqhtml/vscode-extension/-/vscode-extension-2.3.17.tgz",
"integrity": "sha512-6pyOuM5G3hlJx45fRwrTZZfyE/t7IvWK3P/+lsqOIk2uoQcNn8SZjGMnSJyYEdCaPJmA5rHZRok5izGStiEUNg==",
"license": "MIT",
"engines": {
"vscode": "^1.74.0"

View File

@@ -276,13 +276,20 @@ export declare class Jqhtml_Component {
* Lifecycle event callbacks fire after the lifecycle method completes
* If a lifecycle event has already occurred, the callback fires immediately AND registers for future occurrences
* Custom events only fire when explicitly triggered via .trigger()
*
* Callback signature: (component, data?) => void
* - component: The component instance that triggered the event
* - data: Optional data passed as second parameter to trigger()
*/
on(event_name: string, callback: (component: Jqhtml_Component) => void): this;
on(event_name: string, callback: (component: Jqhtml_Component, data?: any) => void): this;
/**
* Trigger a lifecycle event - fires all registered callbacks
* Marks event as occurred so future .on() calls fire immediately
*
* @param event_name - Name of the event to trigger
* @param data - Optional data to pass to callbacks as second parameter
*/
trigger(event_name: string): void;
trigger(event_name: string, data?: any): void;
/**
* Check if any callbacks are registered for a given event
* Used to determine if cleanup logic needs to run

View File

@@ -1 +1 @@
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAcH,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,YAAY,CAAC,EAAE;YACb,GAAG,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;YACjF,UAAU,EAAE,MAAM,IAAI,CAAC;SACxB,CAAC;KACH;CACF;AAED,qBAAa,gBAAgB;IAE3B,MAAM,CAAC,kBAAkB,UAAQ;IACjC,MAAM,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC;IAGtB,CAAC,EAAE,GAAG,CAAC;IACP,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAK;IAGzB,OAAO,CAAC,kBAAkB,CAAmB;IAC7C,OAAO,CAAC,aAAa,CAAiC;IACtD,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,aAAa,CAAoC;IACzD,OAAO,CAAC,iBAAiB,CAAkB;IAC3C,OAAO,CAAC,QAAQ,CAAkB;IAClC,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,mBAAmB,CAAuB;IAClD,OAAO,CAAC,oBAAoB,CAAwE;IACpG,OAAO,CAAC,iBAAiB,CAA0B;IACnD,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,iBAAiB,CAAkB;IAC3C,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,oBAAoB,CAAoC;IAChE,OAAO,CAAC,oBAAoB,CAAuB;IACnD,OAAO,CAAC,uBAAuB,CAAoC;IACnE,OAAO,CAAC,aAAa,CAAkB;IACvC,OAAO,CAAC,iBAAiB,CAAC,CAAsB;IAChD,OAAO,CAAC,yBAAyB,CAAwB;IACzD,OAAO,CAAC,sBAAsB,CAAkB;IAGhD,OAAO,CAAC,UAAU,CAAuB;IAGzC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,iBAAiB,CAAkB;IAC3C,OAAO,CAAC,8BAA8B,CAAkB;IACxD,OAAO,CAAC,WAAW,CAAkB;IAGrC,OAAO,CAAC,mBAAmB,CAAkB;IAG7C,OAAO,CAAC,oBAAoB,CAAkB;IAI9C,OAAO,CAAC,sBAAsB,CAAkB;IAIhD,OAAO,CAAC,WAAW,CAAkB;gBAEzB,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM;IAsJzD;;;;OAIG;IACH,OAAO,CAAC,0BAA0B;IAmClC;;;;;;OAMG;YACW,eAAe;IAO7B;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAO5B;;;OAGG;IACH;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAe5B;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,GAAE,MAAM,GAAG,IAAW,GAAG,MAAM;IA0UzC;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,EAAE,GAAE,MAAM,GAAG,IAAW,GAAG,IAAI;IA+CtC;;;OAGG;IACH,MAAM,CAAC,EAAE,GAAE,MAAM,GAAG,IAAW,GAAG,IAAI;IAItC;;;OAGG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAsJ7B;;;;;OAKG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA0U5B;;;;OAIG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAwD7B;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB3C;;;;OAIG;YACW,wBAAwB;IAqCtC;;;;;;;;;;OAUG;YACW,4BAA4B;IAqC1C;;;;;;;;OAQG;IACG,MAAM,CAAC,aAAa,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBpD;;;;;;;;OAQG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiCG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAqO9B;;;;OAIG;IACH;;;;OAIG;IACH,KAAK,IAAI,IAAI;IA+Cb;;;OAGG;IACH,IAAI,IAAI,IAAI;IAkBZ,SAAS,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IACjC,SAAS,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAC3B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IACxB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAC/B,OAAO,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAE/B;;;;;;;;;OASG;IACH,QAAQ,CAAC,IAAI,MAAM;IAEnB;;;;OAIG;IACH;;;OAGG;IACH,gBAAgB,IAAI,OAAO;IA6B3B;;OAEG;IACH,cAAc,IAAI,MAAM;IAIxB;;;;;;OAMG;IACH,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,SAAS,EAAE,gBAAgB,KAAK,IAAI,GAAG,IAAI;IAsB7E;;;OAGG;IACH,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAiBjC;;;OAGG;IACH,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAK3C;;;;;;;;;;;;;;;OAeG;IACH,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG;IAgB3B;;;;;;;;;;;;;;;OAeG;IACH,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI;IAgB9C;;;OAGG;IACH,YAAY,IAAI,gBAAgB,GAAG,IAAI;IAIvC;;OAEG;IACH,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,EAAE;IAa1C;;OAEG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI;IAoBlD;;OAEG;IACH,MAAM,CAAC,mBAAmB,IAAI,MAAM,EAAE;IA0CtC,OAAO,CAAC,aAAa;IAIrB;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAkB7B,OAAO,CAAC,kBAAkB;IA4B1B,OAAO,CAAC,yBAAyB;IAuHjC,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,gBAAgB;IAcxB;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IA+BzB,OAAO,CAAC,cAAc;IActB,OAAO,CAAC,UAAU;IAUlB;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,0BAA0B;CAqEnC"}
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"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"}

View File

@@ -1955,7 +1955,7 @@ class Jqhtml_Component {
if (window.jqhtml?.debug?.verbose) {
console.log(`[Load Deduplication] Component ${this._cid} (${this.component_name()}) is the leader`, { args: this.args });
}
// Capture state before on_load() for validation
// Capture args state before on_load() for validation
let argsBeforeLoad = null;
try {
argsBeforeLoad = JSON.stringify(this.args);
@@ -1963,7 +1963,6 @@ class Jqhtml_Component {
catch (error) {
// Args contain circular references - skip validation
}
const propertiesBeforeLoad = new Set(Object.keys(this));
// Set loading flag to prevent render() calls during on_load()
this.__loading = true;
// Create restricted proxy to prevent DOM access during on_load()
@@ -2052,7 +2051,6 @@ class Jqhtml_Component {
catch (error) {
// Args contain circular references - skip validation
}
const propertiesAfterLoad = Object.keys(this);
// Check if args were modified (skip if args are non-serializable)
if (argsBeforeLoad !== null && argsAfterLoad !== null && argsBeforeLoad !== argsAfterLoad) {
console.error(`[JQHTML] WARNING: Component "${this.component_name()}" modified this.args in on_load().\n` +
@@ -2062,15 +2060,12 @@ class Jqhtml_Component {
`Fix: Modify this.args in on_create() or other lifecycle methods, not in on_load().\n` +
`this.args stores state that on_load() depends on. Modifying it inside on_load() creates circular dependencies.`);
}
// Check if new properties were added to the component instance
const newProperties = propertiesAfterLoad.filter(prop => !propertiesBeforeLoad.has(prop) && prop !== 'data');
if (newProperties.length > 0) {
console.error(`[JQHTML] WARNING: Component "${this.component_name()}" added new properties in on_load().\n` +
`on_load() should ONLY modify this.data. New properties detected: ${newProperties.join(', ')}\n\n` +
`Fix: Store your data in this.data instead:\n` +
` ❌ this.${newProperties[0]} = value;\n` +
` ✅ this.data.${newProperties[0]} = value;`);
}
// NOTE: We previously checked for new properties being added to the component instance
// during on_load() and warned about them. However, this validation was removed because:
// 1. The restricted proxy already throws errors if code tries to set properties during on_load()
// 2. The validation was producing false positives for properties set during template render
// (templates can legitimately set this.* properties in code blocks like <% this._tabs = [...] %>)
// 3. Properties set via the restricted proxy throw immediately, making post-hoc validation redundant
// DATA MODE: Normalize this.data through serialize/deserialize round-trip
// This ensures "hot" data (fresh from on_load) behaves identically to "cold" data
// (restored from cache). Unregistered class instances become plain objects immediately,
@@ -2649,6 +2644,10 @@ class Jqhtml_Component {
* Lifecycle event callbacks fire after the lifecycle method completes
* If a lifecycle event has already occurred, the callback fires immediately AND registers for future occurrences
* Custom events only fire when explicitly triggered via .trigger()
*
* Callback signature: (component, data?) => void
* - component: The component instance that triggered the event
* - data: Optional data passed as second parameter to trigger()
*/
on(event_name, callback) {
// Initialize callback array for this event if needed
@@ -2659,9 +2658,10 @@ class Jqhtml_Component {
this._lifecycle_callbacks.get(event_name).push(callback);
// If this lifecycle event has already occurred, fire the callback immediately
// (only for lifecycle events - custom events don't have this behavior)
// Note: For already-occurred events, data is undefined since we don't store it
if (this._lifecycle_states.has(event_name)) {
try {
callback(this);
callback(this, undefined);
}
catch (error) {
console.error(`[JQHTML] Error in ${event_name} callback:`, error);
@@ -2672,8 +2672,11 @@ class Jqhtml_Component {
/**
* Trigger a lifecycle event - fires all registered callbacks
* Marks event as occurred so future .on() calls fire immediately
*
* @param event_name - Name of the event to trigger
* @param data - Optional data to pass to callbacks as second parameter
*/
trigger(event_name) {
trigger(event_name, data) {
// Mark this event as occurred
this._lifecycle_states.add(event_name);
// Fire all registered callbacks for this event
@@ -2681,7 +2684,7 @@ class Jqhtml_Component {
if (callbacks) {
for (const callback of callbacks) {
try {
callback.bind(this)(this);
callback.bind(this)(this, data);
}
catch (error) {
console.error(`[JQHTML] Error in ${event_name} callback:`, error);
@@ -4781,7 +4784,7 @@ function init(jQuery) {
}
}
// Version - will be replaced during build with actual version from package.json
const version = '2.3.16';
const version = '2.3.17';
// Default export with all functionality
const jqhtml = {
// Core

File diff suppressed because one or more lines are too long

View File

@@ -1951,7 +1951,7 @@ class Jqhtml_Component {
if (window.jqhtml?.debug?.verbose) {
console.log(`[Load Deduplication] Component ${this._cid} (${this.component_name()}) is the leader`, { args: this.args });
}
// Capture state before on_load() for validation
// Capture args state before on_load() for validation
let argsBeforeLoad = null;
try {
argsBeforeLoad = JSON.stringify(this.args);
@@ -1959,7 +1959,6 @@ class Jqhtml_Component {
catch (error) {
// Args contain circular references - skip validation
}
const propertiesBeforeLoad = new Set(Object.keys(this));
// Set loading flag to prevent render() calls during on_load()
this.__loading = true;
// Create restricted proxy to prevent DOM access during on_load()
@@ -2048,7 +2047,6 @@ class Jqhtml_Component {
catch (error) {
// Args contain circular references - skip validation
}
const propertiesAfterLoad = Object.keys(this);
// Check if args were modified (skip if args are non-serializable)
if (argsBeforeLoad !== null && argsAfterLoad !== null && argsBeforeLoad !== argsAfterLoad) {
console.error(`[JQHTML] WARNING: Component "${this.component_name()}" modified this.args in on_load().\n` +
@@ -2058,15 +2056,12 @@ class Jqhtml_Component {
`Fix: Modify this.args in on_create() or other lifecycle methods, not in on_load().\n` +
`this.args stores state that on_load() depends on. Modifying it inside on_load() creates circular dependencies.`);
}
// Check if new properties were added to the component instance
const newProperties = propertiesAfterLoad.filter(prop => !propertiesBeforeLoad.has(prop) && prop !== 'data');
if (newProperties.length > 0) {
console.error(`[JQHTML] WARNING: Component "${this.component_name()}" added new properties in on_load().\n` +
`on_load() should ONLY modify this.data. New properties detected: ${newProperties.join(', ')}\n\n` +
`Fix: Store your data in this.data instead:\n` +
` ❌ this.${newProperties[0]} = value;\n` +
` ✅ this.data.${newProperties[0]} = value;`);
}
// NOTE: We previously checked for new properties being added to the component instance
// during on_load() and warned about them. However, this validation was removed because:
// 1. The restricted proxy already throws errors if code tries to set properties during on_load()
// 2. The validation was producing false positives for properties set during template render
// (templates can legitimately set this.* properties in code blocks like <% this._tabs = [...] %>)
// 3. Properties set via the restricted proxy throw immediately, making post-hoc validation redundant
// DATA MODE: Normalize this.data through serialize/deserialize round-trip
// This ensures "hot" data (fresh from on_load) behaves identically to "cold" data
// (restored from cache). Unregistered class instances become plain objects immediately,
@@ -2645,6 +2640,10 @@ class Jqhtml_Component {
* Lifecycle event callbacks fire after the lifecycle method completes
* If a lifecycle event has already occurred, the callback fires immediately AND registers for future occurrences
* Custom events only fire when explicitly triggered via .trigger()
*
* Callback signature: (component, data?) => void
* - component: The component instance that triggered the event
* - data: Optional data passed as second parameter to trigger()
*/
on(event_name, callback) {
// Initialize callback array for this event if needed
@@ -2655,9 +2654,10 @@ class Jqhtml_Component {
this._lifecycle_callbacks.get(event_name).push(callback);
// If this lifecycle event has already occurred, fire the callback immediately
// (only for lifecycle events - custom events don't have this behavior)
// Note: For already-occurred events, data is undefined since we don't store it
if (this._lifecycle_states.has(event_name)) {
try {
callback(this);
callback(this, undefined);
}
catch (error) {
console.error(`[JQHTML] Error in ${event_name} callback:`, error);
@@ -2668,8 +2668,11 @@ class Jqhtml_Component {
/**
* Trigger a lifecycle event - fires all registered callbacks
* Marks event as occurred so future .on() calls fire immediately
*
* @param event_name - Name of the event to trigger
* @param data - Optional data to pass to callbacks as second parameter
*/
trigger(event_name) {
trigger(event_name, data) {
// Mark this event as occurred
this._lifecycle_states.add(event_name);
// Fire all registered callbacks for this event
@@ -2677,7 +2680,7 @@ class Jqhtml_Component {
if (callbacks) {
for (const callback of callbacks) {
try {
callback.bind(this)(this);
callback.bind(this)(this, data);
}
catch (error) {
console.error(`[JQHTML] Error in ${event_name} callback:`, error);
@@ -4777,7 +4780,7 @@ function init(jQuery) {
}
}
// Version - will be replaced during build with actual version from package.json
const version = '2.3.16';
const version = '2.3.17';
// Default export with all functionality
const jqhtml = {
// Core

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
/**
* JQHTML Core v2.3.16
* JQHTML Core v2.3.17
* (c) 2025 JQHTML Team
* Released under the MIT License
*/
@@ -1956,7 +1956,7 @@ class Jqhtml_Component {
if (window.jqhtml?.debug?.verbose) {
console.log(`[Load Deduplication] Component ${this._cid} (${this.component_name()}) is the leader`, { args: this.args });
}
// Capture state before on_load() for validation
// Capture args state before on_load() for validation
let argsBeforeLoad = null;
try {
argsBeforeLoad = JSON.stringify(this.args);
@@ -1964,7 +1964,6 @@ class Jqhtml_Component {
catch (error) {
// Args contain circular references - skip validation
}
const propertiesBeforeLoad = new Set(Object.keys(this));
// Set loading flag to prevent render() calls during on_load()
this.__loading = true;
// Create restricted proxy to prevent DOM access during on_load()
@@ -2053,7 +2052,6 @@ class Jqhtml_Component {
catch (error) {
// Args contain circular references - skip validation
}
const propertiesAfterLoad = Object.keys(this);
// Check if args were modified (skip if args are non-serializable)
if (argsBeforeLoad !== null && argsAfterLoad !== null && argsBeforeLoad !== argsAfterLoad) {
console.error(`[JQHTML] WARNING: Component "${this.component_name()}" modified this.args in on_load().\n` +
@@ -2063,15 +2061,12 @@ class Jqhtml_Component {
`Fix: Modify this.args in on_create() or other lifecycle methods, not in on_load().\n` +
`this.args stores state that on_load() depends on. Modifying it inside on_load() creates circular dependencies.`);
}
// Check if new properties were added to the component instance
const newProperties = propertiesAfterLoad.filter(prop => !propertiesBeforeLoad.has(prop) && prop !== 'data');
if (newProperties.length > 0) {
console.error(`[JQHTML] WARNING: Component "${this.component_name()}" added new properties in on_load().\n` +
`on_load() should ONLY modify this.data. New properties detected: ${newProperties.join(', ')}\n\n` +
`Fix: Store your data in this.data instead:\n` +
` ❌ this.${newProperties[0]} = value;\n` +
` ✅ this.data.${newProperties[0]} = value;`);
}
// NOTE: We previously checked for new properties being added to the component instance
// during on_load() and warned about them. However, this validation was removed because:
// 1. The restricted proxy already throws errors if code tries to set properties during on_load()
// 2. The validation was producing false positives for properties set during template render
// (templates can legitimately set this.* properties in code blocks like <% this._tabs = [...] %>)
// 3. Properties set via the restricted proxy throw immediately, making post-hoc validation redundant
// DATA MODE: Normalize this.data through serialize/deserialize round-trip
// This ensures "hot" data (fresh from on_load) behaves identically to "cold" data
// (restored from cache). Unregistered class instances become plain objects immediately,
@@ -2650,6 +2645,10 @@ class Jqhtml_Component {
* Lifecycle event callbacks fire after the lifecycle method completes
* If a lifecycle event has already occurred, the callback fires immediately AND registers for future occurrences
* Custom events only fire when explicitly triggered via .trigger()
*
* Callback signature: (component, data?) => void
* - component: The component instance that triggered the event
* - data: Optional data passed as second parameter to trigger()
*/
on(event_name, callback) {
// Initialize callback array for this event if needed
@@ -2660,9 +2659,10 @@ class Jqhtml_Component {
this._lifecycle_callbacks.get(event_name).push(callback);
// If this lifecycle event has already occurred, fire the callback immediately
// (only for lifecycle events - custom events don't have this behavior)
// Note: For already-occurred events, data is undefined since we don't store it
if (this._lifecycle_states.has(event_name)) {
try {
callback(this);
callback(this, undefined);
}
catch (error) {
console.error(`[JQHTML] Error in ${event_name} callback:`, error);
@@ -2673,8 +2673,11 @@ class Jqhtml_Component {
/**
* Trigger a lifecycle event - fires all registered callbacks
* Marks event as occurred so future .on() calls fire immediately
*
* @param event_name - Name of the event to trigger
* @param data - Optional data to pass to callbacks as second parameter
*/
trigger(event_name) {
trigger(event_name, data) {
// Mark this event as occurred
this._lifecycle_states.add(event_name);
// Fire all registered callbacks for this event
@@ -2682,7 +2685,7 @@ class Jqhtml_Component {
if (callbacks) {
for (const callback of callbacks) {
try {
callback.bind(this)(this);
callback.bind(this)(this, data);
}
catch (error) {
console.error(`[JQHTML] Error in ${event_name} callback:`, error);
@@ -4782,7 +4785,7 @@ function init(jQuery) {
}
}
// Version - will be replaced during build with actual version from package.json
const version = '2.3.16';
const version = '2.3.17';
// Default export with all functionality
const jqhtml = {
// Core

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
{
"name": "@jqhtml/core",
"version": "2.3.16",
"version": "2.3.17",
"description": "Core runtime library for JQHTML",
"type": "module",
"main": "./dist/index.js",

View File

@@ -1377,7 +1377,7 @@ export class CodeGenerator {
for (const [name, component] of this.components) {
code += `// Component: ${name}\n`;
code += `jqhtml_components.set('${name}', {\n`;
code += ` _jqhtml_version: '2.3.16',\n`; // Version will be replaced during build
code += ` _jqhtml_version: '2.3.17',\n`; // Version will be replaced during build
code += ` name: '${name}',\n`;
code += ` tag: '${component.tagName}',\n`;
code += ` defaultAttributes: ${this.serializeAttributeObject(component.defaultAttributes)},\n`;

View File

@@ -1,6 +1,6 @@
{
"name": "@jqhtml/parser",
"version": "2.3.16",
"version": "2.3.17",
"description": "JQHTML template parser - converts templates to JavaScript",
"type": "module",
"main": "dist/index.js",

View File

@@ -1 +1 @@
2.3.16
2.3.17

View File

@@ -2,7 +2,7 @@
"name": "@jqhtml/vscode-extension",
"displayName": "JQHTML",
"description": "Syntax highlighting and language support for JQHTML template files",
"version": "2.3.16",
"version": "2.3.17",
"publisher": "jqhtml",
"license": "MIT",
"publishConfig": {