@mintjamsinc/ichigojs 0.1.21 → 0.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -0
- package/dist/ichigo.esm.js +149 -17
- package/dist/ichigo.esm.js.map +1 -1
- package/dist/ichigo.esm.min.js +1 -2
- package/dist/ichigo.esm.min.js.map +1 -1
- package/dist/ichigo.umd.js +149 -16
- package/dist/ichigo.umd.js.map +1 -1
- package/dist/ichigo.umd.min.js +1 -2
- package/dist/ichigo.umd.min.js.map +1 -1
- package/dist/types/ichigo/VApplication.d.ts +16 -5
- package/dist/types/ichigo/VApplicationInit.d.ts +25 -0
- package/dist/types/ichigo/VBindings.d.ts +7 -0
- package/dist/types/ichigo/util/ReactiveProxy.d.ts +29 -0
- package/dist/types/index.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -90,6 +90,51 @@ VDOM.createApp({
|
|
|
90
90
|
}).mount('#app');
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
+
### Marking Objects as Non-Reactive
|
|
94
|
+
|
|
95
|
+
Use `$markRaw()` to prevent objects from being wrapped in a reactive proxy. This is useful when storing third-party library instances or large data structures that don't need reactivity.
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
VDOM.createApp({
|
|
99
|
+
data() {
|
|
100
|
+
return {
|
|
101
|
+
// Regular reactive data
|
|
102
|
+
count: 0,
|
|
103
|
+
|
|
104
|
+
// Mark as non-reactive
|
|
105
|
+
chart: null,
|
|
106
|
+
bigData: null
|
|
107
|
+
};
|
|
108
|
+
},
|
|
109
|
+
methods: {
|
|
110
|
+
initChart($ctx) {
|
|
111
|
+
// Create Chart.js instance and mark as non-reactive
|
|
112
|
+
const chartInstance = new Chart(canvas, { /* ... */ });
|
|
113
|
+
this.chart = this.$markRaw(chartInstance);
|
|
114
|
+
|
|
115
|
+
// Large dataset that doesn't need reactivity
|
|
116
|
+
const data = fetchLargeDataset();
|
|
117
|
+
this.bigData = this.$markRaw(data);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}).mount('#app');
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**When to use `$markRaw()`:**
|
|
124
|
+
|
|
125
|
+
- Third-party library instances (Chart.js, Three.js, etc.)
|
|
126
|
+
- Large arrays or objects that don't need change detection
|
|
127
|
+
- Objects with complex internal state that shouldn't be proxied
|
|
128
|
+
- DOM elements or other built-in objects with internal slots
|
|
129
|
+
|
|
130
|
+
**Note:** Objects marked with `$markRaw()` won't trigger updates when modified. Use reactive properties to trigger updates when needed:
|
|
131
|
+
|
|
132
|
+
```javascript
|
|
133
|
+
// Update reactive trigger after modifying non-reactive data
|
|
134
|
+
this.bigData.push(newItem); // Won't trigger update
|
|
135
|
+
this.count++; // Triggers update
|
|
136
|
+
```
|
|
137
|
+
|
|
93
138
|
### Computed Properties
|
|
94
139
|
|
|
95
140
|
Computed properties automatically track their dependencies and re-evaluate when dependencies change.
|
package/dist/ichigo.esm.js
CHANGED
|
@@ -7417,6 +7417,16 @@ class ReactiveProxy {
|
|
|
7417
7417
|
* This prevents creating multiple proxies for the same object accessed from different paths.
|
|
7418
7418
|
*/
|
|
7419
7419
|
static proxyCache = new WeakMap();
|
|
7420
|
+
/**
|
|
7421
|
+
* A WeakMap to track which objects are proxies, mapping proxy -> original target.
|
|
7422
|
+
* This prevents double-wrapping of already proxied objects.
|
|
7423
|
+
*/
|
|
7424
|
+
static proxyToTarget = new WeakMap();
|
|
7425
|
+
/**
|
|
7426
|
+
* A WeakSet to track objects marked as "raw" (non-reactive).
|
|
7427
|
+
* These objects will not be wrapped with Proxy.
|
|
7428
|
+
*/
|
|
7429
|
+
static rawObjects = new WeakSet();
|
|
7420
7430
|
/**
|
|
7421
7431
|
* Creates a reactive proxy for the given object.
|
|
7422
7432
|
* The proxy will call the onChange callback whenever a property is modified.
|
|
@@ -7431,9 +7441,19 @@ class ReactiveProxy {
|
|
|
7431
7441
|
if (typeof target !== 'object' || target === null) {
|
|
7432
7442
|
return target;
|
|
7433
7443
|
}
|
|
7444
|
+
// Don't wrap objects marked as raw (non-reactive)
|
|
7445
|
+
if (this.rawObjects.has(target)) {
|
|
7446
|
+
return target;
|
|
7447
|
+
}
|
|
7434
7448
|
// Don't wrap built-in objects that have internal slots
|
|
7435
7449
|
// These objects require their methods to be called with the correct 'this' context
|
|
7436
|
-
|
|
7450
|
+
// Use Object.prototype.toString for more reliable type checking
|
|
7451
|
+
const typeTag = Object.prototype.toString.call(target);
|
|
7452
|
+
if (typeTag === '[object Date]' || typeTag === '[object RegExp]' || typeTag === '[object Error]') {
|
|
7453
|
+
return target;
|
|
7454
|
+
}
|
|
7455
|
+
// Check if the target is already a proxy - if so, return it as-is to prevent double-wrapping
|
|
7456
|
+
if (this.proxyToTarget.has(target)) {
|
|
7437
7457
|
return target;
|
|
7438
7458
|
}
|
|
7439
7459
|
// Check if we already have a proxy for this target with this path
|
|
@@ -7454,8 +7474,14 @@ class ReactiveProxy {
|
|
|
7454
7474
|
const value = Reflect.get(obj, key);
|
|
7455
7475
|
// If the value is an object or array, make it reactive too
|
|
7456
7476
|
if (typeof value === 'object' && value !== null) {
|
|
7477
|
+
// Don't wrap objects marked as raw (non-reactive)
|
|
7478
|
+
if (ReactiveProxy.rawObjects.has(value)) {
|
|
7479
|
+
return value;
|
|
7480
|
+
}
|
|
7457
7481
|
// Don't wrap built-in objects that have internal slots
|
|
7458
|
-
|
|
7482
|
+
// Use Object.prototype.toString for more reliable type checking
|
|
7483
|
+
const valueTypeTag = Object.prototype.toString.call(value);
|
|
7484
|
+
if (valueTypeTag === '[object Date]' || valueTypeTag === '[object RegExp]' || valueTypeTag === '[object Error]') {
|
|
7459
7485
|
return value;
|
|
7460
7486
|
}
|
|
7461
7487
|
// Build the nested path
|
|
@@ -7468,7 +7494,7 @@ class ReactiveProxy {
|
|
|
7468
7494
|
const arrayMutationMethods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'];
|
|
7469
7495
|
if (arrayMutationMethods.includes(key)) {
|
|
7470
7496
|
return function (...args) {
|
|
7471
|
-
const result = value.apply(
|
|
7497
|
+
const result = value.apply(obj, args);
|
|
7472
7498
|
onChange(path || undefined);
|
|
7473
7499
|
return result;
|
|
7474
7500
|
};
|
|
@@ -7497,6 +7523,8 @@ class ReactiveProxy {
|
|
|
7497
7523
|
});
|
|
7498
7524
|
// Cache the proxy for this path
|
|
7499
7525
|
pathMap.set(path, proxy);
|
|
7526
|
+
// Track that this proxy wraps the target to prevent double-wrapping
|
|
7527
|
+
this.proxyToTarget.set(proxy, target);
|
|
7500
7528
|
return proxy;
|
|
7501
7529
|
}
|
|
7502
7530
|
/**
|
|
@@ -7520,6 +7548,32 @@ class ReactiveProxy {
|
|
|
7520
7548
|
// In a full implementation, we'd need to store a reverse mapping
|
|
7521
7549
|
return obj;
|
|
7522
7550
|
}
|
|
7551
|
+
/**
|
|
7552
|
+
* Marks an object as "raw" (non-reactive).
|
|
7553
|
+
* Objects marked as raw will not be wrapped with Proxy when accessed from reactive objects.
|
|
7554
|
+
* This is useful for objects that should not be reactive, such as:
|
|
7555
|
+
* - Objects with private fields (class instances with # fields)
|
|
7556
|
+
* - Third-party library instances
|
|
7557
|
+
* - Objects used only for method calls
|
|
7558
|
+
*
|
|
7559
|
+
* @param obj The object to mark as raw.
|
|
7560
|
+
* @returns The same object (for chaining).
|
|
7561
|
+
*/
|
|
7562
|
+
static markRaw(obj) {
|
|
7563
|
+
if (typeof obj === 'object' && obj !== null) {
|
|
7564
|
+
this.rawObjects.add(obj);
|
|
7565
|
+
}
|
|
7566
|
+
return obj;
|
|
7567
|
+
}
|
|
7568
|
+
/**
|
|
7569
|
+
* Checks if an object is marked as raw (non-reactive).
|
|
7570
|
+
*
|
|
7571
|
+
* @param obj The object to check.
|
|
7572
|
+
* @returns True if the object is marked as raw, false otherwise.
|
|
7573
|
+
*/
|
|
7574
|
+
static isRaw(obj) {
|
|
7575
|
+
return typeof obj === 'object' && obj !== null && this.rawObjects.has(obj);
|
|
7576
|
+
}
|
|
7523
7577
|
}
|
|
7524
7578
|
|
|
7525
7579
|
// Copyright (c) 2025 MintJams Inc. Licensed under MIT License.
|
|
@@ -7553,6 +7607,10 @@ class VBindings {
|
|
|
7553
7607
|
* Cache for array lengths to detect length changes when the same object reference is used.
|
|
7554
7608
|
*/
|
|
7555
7609
|
#lengthCache = new Map();
|
|
7610
|
+
/**
|
|
7611
|
+
* Flag to suppress onChange callbacks temporarily.
|
|
7612
|
+
*/
|
|
7613
|
+
#suppressOnChange = false;
|
|
7556
7614
|
/**
|
|
7557
7615
|
* Creates a new instance of VBindings.
|
|
7558
7616
|
* @param parent The parent bindings, if any.
|
|
@@ -7591,7 +7649,9 @@ class VBindings {
|
|
|
7591
7649
|
this.#logger?.debug(`Binding changed: ${path}`);
|
|
7592
7650
|
this.#changes.add(path);
|
|
7593
7651
|
}
|
|
7594
|
-
this.#
|
|
7652
|
+
if (!this.#suppressOnChange) {
|
|
7653
|
+
this.#onChange?.(changedPath);
|
|
7654
|
+
}
|
|
7595
7655
|
}, key);
|
|
7596
7656
|
}
|
|
7597
7657
|
const oldValue = Reflect.get(target, key);
|
|
@@ -7616,7 +7676,9 @@ class VBindings {
|
|
|
7616
7676
|
this.#logger.debug(`Binding set on ${target === obj ? 'local' : 'parent'}: ${key}: ${oldValuePreview} -> ${newValuePreview}`);
|
|
7617
7677
|
}
|
|
7618
7678
|
this.#changes.add(key);
|
|
7619
|
-
this.#
|
|
7679
|
+
if (!this.#suppressOnChange) {
|
|
7680
|
+
this.#onChange?.(key);
|
|
7681
|
+
}
|
|
7620
7682
|
}
|
|
7621
7683
|
return result;
|
|
7622
7684
|
},
|
|
@@ -7703,6 +7765,21 @@ class VBindings {
|
|
|
7703
7765
|
remove(key) {
|
|
7704
7766
|
delete this.#local[key];
|
|
7705
7767
|
}
|
|
7768
|
+
/**
|
|
7769
|
+
* Sets a binding value without triggering onChange callback.
|
|
7770
|
+
* This is useful for internal updates that shouldn't trigger reactivity.
|
|
7771
|
+
* @param key The binding name.
|
|
7772
|
+
* @param value The binding value.
|
|
7773
|
+
*/
|
|
7774
|
+
setSilent(key, value) {
|
|
7775
|
+
this.#suppressOnChange = true;
|
|
7776
|
+
try {
|
|
7777
|
+
this.#local[key] = value;
|
|
7778
|
+
}
|
|
7779
|
+
finally {
|
|
7780
|
+
this.#suppressOnChange = false;
|
|
7781
|
+
}
|
|
7782
|
+
}
|
|
7706
7783
|
}
|
|
7707
7784
|
|
|
7708
7785
|
// Copyright (c) 2025 MintJams Inc. Licensed under MIT License.
|
|
@@ -9224,7 +9301,10 @@ class VForDirective {
|
|
|
9224
9301
|
vApplication: this.#vNode.vApplication,
|
|
9225
9302
|
parentVNode: this.#vNode.parentVNode,
|
|
9226
9303
|
bindings,
|
|
9227
|
-
dependentIdentifiers: [
|
|
9304
|
+
dependentIdentifiers: [
|
|
9305
|
+
`${this.#sourceName}[${context.index}]`,
|
|
9306
|
+
...this.#vNode.vApplication.resolveDependentIdentifiers(this.#sourceName, context.item)
|
|
9307
|
+
],
|
|
9228
9308
|
});
|
|
9229
9309
|
newRenderedItems.set(key, vNode);
|
|
9230
9310
|
vNode.forceUpdate();
|
|
@@ -11072,6 +11152,10 @@ class VApplication {
|
|
|
11072
11152
|
* The application options.
|
|
11073
11153
|
*/
|
|
11074
11154
|
#options;
|
|
11155
|
+
/**
|
|
11156
|
+
* The parent application, if any.
|
|
11157
|
+
*/
|
|
11158
|
+
#parentApplication;
|
|
11075
11159
|
/**
|
|
11076
11160
|
* The global directive parser registry.
|
|
11077
11161
|
*/
|
|
@@ -11110,12 +11194,12 @@ class VApplication {
|
|
|
11110
11194
|
#updateScheduled = false;
|
|
11111
11195
|
/**
|
|
11112
11196
|
* Creates an instance of the virtual application.
|
|
11113
|
-
* @param
|
|
11114
|
-
* @param directiveParserRegistry The global directive parser registry.
|
|
11115
|
-
* @param componentRegistry The global component registry.
|
|
11197
|
+
* @param args The initialization arguments for the application.
|
|
11116
11198
|
*/
|
|
11117
|
-
constructor(
|
|
11199
|
+
constructor(args) {
|
|
11200
|
+
const { options, parentApplication: parentApplication, directiveParserRegistry, componentRegistry } = args;
|
|
11118
11201
|
this.#options = options;
|
|
11202
|
+
this.#parentApplication = parentApplication;
|
|
11119
11203
|
this.#directiveParserRegistry = directiveParserRegistry;
|
|
11120
11204
|
this.#componentRegistry = componentRegistry;
|
|
11121
11205
|
// Initialize log manager and logger
|
|
@@ -11128,6 +11212,12 @@ class VApplication {
|
|
|
11128
11212
|
// Initialize bindings from data, computed, and methods
|
|
11129
11213
|
this.#initializeBindings();
|
|
11130
11214
|
}
|
|
11215
|
+
/**
|
|
11216
|
+
* Gets the parent application, if any.
|
|
11217
|
+
*/
|
|
11218
|
+
get parentApplication() {
|
|
11219
|
+
return this.#parentApplication;
|
|
11220
|
+
}
|
|
11131
11221
|
/**
|
|
11132
11222
|
* Gets the global directive parser registry.
|
|
11133
11223
|
*/
|
|
@@ -11207,7 +11297,12 @@ class VApplication {
|
|
|
11207
11297
|
* @returns The created child application instance.
|
|
11208
11298
|
*/
|
|
11209
11299
|
createChildApp(options) {
|
|
11210
|
-
return new VApplication(
|
|
11300
|
+
return new VApplication({
|
|
11301
|
+
options,
|
|
11302
|
+
parentApplication: this,
|
|
11303
|
+
directiveParserRegistry: this.#directiveParserRegistry,
|
|
11304
|
+
componentRegistry: this.#componentRegistry
|
|
11305
|
+
});
|
|
11211
11306
|
}
|
|
11212
11307
|
/**
|
|
11213
11308
|
* Cleans the element by removing unnecessary whitespace text nodes.
|
|
@@ -11239,6 +11334,26 @@ class VApplication {
|
|
|
11239
11334
|
}
|
|
11240
11335
|
}
|
|
11241
11336
|
}
|
|
11337
|
+
/**
|
|
11338
|
+
* Computes dependent identifiers for a given computed property and value.
|
|
11339
|
+
* This is used to track dependencies in directives like v-for.
|
|
11340
|
+
* @param computedName The name of the computed property.
|
|
11341
|
+
* @param value The value to check for dependencies.
|
|
11342
|
+
* @returns An array of dependent identifiers.
|
|
11343
|
+
*/
|
|
11344
|
+
resolveDependentIdentifiers(computedName, value) {
|
|
11345
|
+
const identifiers = [];
|
|
11346
|
+
for (const dep of this.#computedDependencies[computedName] || []) {
|
|
11347
|
+
const depValue = this.#bindings?.get(dep);
|
|
11348
|
+
if (Array.isArray(depValue)) {
|
|
11349
|
+
const idx = depValue.indexOf(value);
|
|
11350
|
+
if (idx !== -1) {
|
|
11351
|
+
identifiers.push(`${dep}[${idx}]`);
|
|
11352
|
+
}
|
|
11353
|
+
}
|
|
11354
|
+
}
|
|
11355
|
+
return identifiers;
|
|
11356
|
+
}
|
|
11242
11357
|
/**
|
|
11243
11358
|
* Initializes bindings from data, computed properties, and methods.
|
|
11244
11359
|
* @returns The initialized bindings object.
|
|
@@ -11253,6 +11368,7 @@ class VApplication {
|
|
|
11253
11368
|
});
|
|
11254
11369
|
// Inject utility methods into bindings
|
|
11255
11370
|
this.#bindings.set('$nextTick', (callback) => this.#nextTick(callback));
|
|
11371
|
+
this.#bindings.set('$markRaw', (obj) => ReactiveProxy.markRaw(obj));
|
|
11256
11372
|
// Add methods
|
|
11257
11373
|
if (this.#options.methods) {
|
|
11258
11374
|
for (const [key, method] of Object.entries(this.#options.methods)) {
|
|
@@ -11267,7 +11383,13 @@ class VApplication {
|
|
|
11267
11383
|
}
|
|
11268
11384
|
// Add data properties
|
|
11269
11385
|
if (this.#options.data) {
|
|
11270
|
-
|
|
11386
|
+
// Create a $ctx context object with utility functions for data()
|
|
11387
|
+
// This provides the same $markRaw access as in lifecycle hooks (@mount, etc.)
|
|
11388
|
+
const $ctx = {
|
|
11389
|
+
$markRaw: (obj) => ReactiveProxy.markRaw(obj)
|
|
11390
|
+
};
|
|
11391
|
+
// Call data() with $ctx as 'this'
|
|
11392
|
+
const data = this.#options.data.call($ctx);
|
|
11271
11393
|
if (data && typeof data === 'object') {
|
|
11272
11394
|
for (const [key, value] of Object.entries(data)) {
|
|
11273
11395
|
this.#bindings.set(key, value);
|
|
@@ -11350,9 +11472,15 @@ class VApplication {
|
|
|
11350
11472
|
try {
|
|
11351
11473
|
const oldValue = this.#bindings?.get(key);
|
|
11352
11474
|
const newValue = computedFn.call(this.#bindings?.raw);
|
|
11353
|
-
//
|
|
11354
|
-
|
|
11355
|
-
|
|
11475
|
+
// Check if the value actually changed
|
|
11476
|
+
let hasChanged = oldValue !== newValue;
|
|
11477
|
+
// For arrays, always update (VBindings will detect length changes via its length cache)
|
|
11478
|
+
if (!hasChanged && Array.isArray(newValue)) {
|
|
11479
|
+
hasChanged = true;
|
|
11480
|
+
}
|
|
11481
|
+
if (hasChanged) {
|
|
11482
|
+
// Use setSilent to avoid triggering onChange during computed property updates
|
|
11483
|
+
this.#bindings?.setSilent(key, newValue);
|
|
11356
11484
|
allChanges.add(key);
|
|
11357
11485
|
}
|
|
11358
11486
|
}
|
|
@@ -11441,9 +11569,13 @@ class VDOM {
|
|
|
11441
11569
|
* @returns The created virtual application instance.
|
|
11442
11570
|
*/
|
|
11443
11571
|
static createApp(options) {
|
|
11444
|
-
return new VApplication(
|
|
11572
|
+
return new VApplication({
|
|
11573
|
+
options,
|
|
11574
|
+
directiveParserRegistry: this.#directiveParserRegistry,
|
|
11575
|
+
componentRegistry: this.#componentRegistry
|
|
11576
|
+
});
|
|
11445
11577
|
}
|
|
11446
11578
|
}
|
|
11447
11579
|
|
|
11448
|
-
export { VComponent, VComponentRegistry, VDOM };
|
|
11580
|
+
export { ReactiveProxy, VComponent, VComponentRegistry, VDOM };
|
|
11449
11581
|
//# sourceMappingURL=ichigo.esm.js.map
|