@mintjamsinc/ichigojs 0.1.84 → 0.1.86

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/dist/ichigo.cjs CHANGED
@@ -6766,28 +6766,29 @@
6766
6766
  }
6767
6767
  }
6768
6768
  else if (node.type === 'MemberExpression') {
6769
- // Reconstruct chain parts from a MemberExpression (e.g. a.b.c)
6769
+ // Reconstruct chain parts from a MemberExpression (e.g. a.b.c). Each part
6770
+ // carries its own separator so that the chain is spelled the way the reactive
6771
+ // proxy reports changes: array indices in brackets, properties after a dot
6772
+ // (e.g. "items[0].name").
6770
6773
  const parts = [];
6771
6774
  let cur = node;
6772
- let stop = false;
6773
- while (cur && !stop) {
6775
+ while (cur) {
6774
6776
  // property part (right side)
6775
6777
  if (cur.property) {
6776
6778
  if (!cur.computed && cur.property.type === 'Identifier') {
6777
- parts.unshift(cur.property.name);
6779
+ parts.unshift(`.${cur.property.name}`);
6780
+ }
6781
+ else if (cur.computed && cur.property.type === 'Literal' && typeof cur.property.value === 'number') {
6782
+ parts.unshift(`[${cur.property.value}]`);
6778
6783
  }
6779
6784
  else if (cur.computed && cur.property.type === 'Literal') {
6780
- parts.unshift(String(cur.property.value));
6785
+ parts.unshift(`.${String(cur.property.value)}`);
6781
6786
  }
6782
- else if (cur.computed && cur.property.type === 'Identifier') {
6783
- // e.g. obj[prop] -> include the prop identifier (it will also be added separately)
6784
- parts.unshift(cur.property.name);
6785
- }
6786
- else {
6787
- // unknown property shape (e.g. expression) — stop adding chain here
6788
- // inner expressions will be visited separately by walk.full
6789
- stop = true;
6790
- break;
6787
+ else if (cur.computed) {
6788
+ // e.g. items[i] or items[i + 1]: the index is only known when the
6789
+ // expression runs, so "[*]" stands for any index or key. Identifiers
6790
+ // inside the index expression are visited separately by walk.full.
6791
+ parts.unshift('[*]');
6791
6792
  }
6792
6793
  }
6793
6794
  // object part (left side)
@@ -6807,7 +6808,6 @@
6807
6808
  }
6808
6809
  else {
6809
6810
  // other object types (CallExpression, etc.) — stop here
6810
- stop = true;
6811
6811
  break;
6812
6812
  }
6813
6813
  }
@@ -6815,10 +6815,15 @@
6815
6815
  break;
6816
6816
  }
6817
6817
  }
6818
- if (parts.length > 0) {
6819
- // Add progressive chains: 'a', 'a.b', 'a.b.c'
6818
+ // A chain that does not start at a name (e.g. `this.a.b` or `f().b`) begins
6819
+ // with its first property; one that begins with an index names no binding.
6820
+ if (parts.length > 0 && parts[0].startsWith('.')) {
6821
+ parts[0] = parts[0].substring(1);
6822
+ }
6823
+ if (parts.length > 0 && !parts[0].startsWith('[')) {
6824
+ // Add progressive chains: 'a', 'a.b', 'a.b[0]', 'a.b[0].c'
6820
6825
  for (let i = 0; i < parts.length; i++) {
6821
- const chain = parts.slice(0, i + 1).join('.');
6826
+ const chain = parts.slice(0, i + 1).join('');
6822
6827
  identifiers.add(chain);
6823
6828
  // Also apply functionDependencies lookup for the chain key
6824
6829
  if (functionDependencies[chain]) {
@@ -6832,6 +6837,16 @@
6832
6837
  });
6833
6838
  return Array.from(identifiers);
6834
6839
  }
6840
+ /**
6841
+ * Returns the binding name an identifier chain starts with
6842
+ * (e.g. "items" for "items[0].name" or "user.name").
6843
+ * @param identifier An identifier or member chain returned by extractIdentifiers.
6844
+ * @returns The left-most name of the chain.
6845
+ */
6846
+ static baseIdentifier(identifier) {
6847
+ const end = identifier.search(/[.[]/);
6848
+ return end === -1 ? identifier : identifier.substring(0, end);
6849
+ }
6835
6850
  /**
6836
6851
  * Analyzes the dependencies of functions in the provided dictionary.
6837
6852
  * @param functions A dictionary mapping function names to their implementations.
@@ -7246,7 +7261,7 @@
7246
7261
  // Use the base (left-most) segment of member chains and preserve order.
7247
7262
  const baseIdentifiers = [];
7248
7263
  for (const id of extractedIdentifiers) {
7249
- const base = id.split('.')[0];
7264
+ const base = ExpressionUtils.baseIdentifier(id);
7250
7265
  if (!baseIdentifiers.includes(base)) {
7251
7266
  baseIdentifiers.push(base);
7252
7267
  }
@@ -7787,13 +7802,20 @@
7787
7802
  }
7788
7803
  /**
7789
7804
  * Updates a regular attribute.
7805
+ * An attribute that already holds the value is left alone: setting `src` on
7806
+ * an <iframe>, even to the same URL, loads its page again.
7790
7807
  */
7791
7808
  #updateAttribute(element, name, value) {
7792
7809
  if (value == null || value === false) {
7793
- element.removeAttribute(name);
7810
+ if (element.hasAttribute(name)) {
7811
+ element.removeAttribute(name);
7812
+ }
7794
7813
  }
7795
7814
  else {
7796
- element.setAttribute(name, String(value));
7815
+ const text = String(value);
7816
+ if (element.getAttribute(name) !== text) {
7817
+ element.setAttribute(name, text);
7818
+ }
7797
7819
  }
7798
7820
  }
7799
7821
  }
@@ -8033,6 +8055,11 @@
8033
8055
  * This allows mapping variable names to their actual source paths for dependency tracking.
8034
8056
  */
8035
8057
  static pathAliases = new Map();
8058
+ /**
8059
+ * Compiled patterns for identifiers that contain a "[*]" index, keyed by the
8060
+ * identifier and whether descendants of the path also match.
8061
+ */
8062
+ static pathPatterns = new Map();
8036
8063
  /**
8037
8064
  * The `Object.prototype.toString` tags of the *only* value types we deeply
8038
8065
  * proxy. Everything else is returned untouched (raw).
@@ -8395,24 +8422,47 @@
8395
8422
  */
8396
8423
  static doesChangeMatchIdentifier(changePath, identifier) {
8397
8424
  // Direct match
8398
- if (changePath === identifier) {
8425
+ if (this.pathMatches(changePath, identifier)) {
8399
8426
  return true;
8400
8427
  }
8401
- // Check if the identifier has an alias that matches
8428
+ // Check if the change path is the resolved identifier or lies below it
8429
+ // (handles cases like array item changes)
8402
8430
  const resolvedIdentifier = this.resolvePathAlias(identifier);
8403
- if (resolvedIdentifier && changePath === resolvedIdentifier) {
8431
+ if (resolvedIdentifier && this.pathMatches(changePath, resolvedIdentifier, true)) {
8404
8432
  return true;
8405
8433
  }
8406
- // Check if change path starts with the resolved identifier
8407
- // (handles cases like array item changes)
8408
- if (resolvedIdentifier) {
8409
- if (changePath.startsWith(resolvedIdentifier + '.') ||
8410
- changePath.startsWith(resolvedIdentifier + '[')) {
8411
- return true;
8412
- }
8413
- }
8414
8434
  return false;
8415
8435
  }
8436
+ /**
8437
+ * Checks whether a change path names the location an identifier refers to.
8438
+ * A "[*]" index in the identifier (e.g. "items[*].name" for `items[i].name`)
8439
+ * stands for an index known only when the expression runs and matches any
8440
+ * one index or key.
8441
+ *
8442
+ * @param changePath The path that was changed (e.g., "items[2].name")
8443
+ * @param identifier The identifier to check (e.g., "items[*].name")
8444
+ * @param descendants Whether a change below the identifier also matches
8445
+ * @returns True if the change path matches the identifier
8446
+ */
8447
+ static pathMatches(changePath, identifier, descendants = false) {
8448
+ if (changePath === identifier) {
8449
+ return true;
8450
+ }
8451
+ if (!identifier.includes('[*]')) {
8452
+ return descendants && (changePath.startsWith(identifier + '.') || changePath.startsWith(identifier + '['));
8453
+ }
8454
+ const cacheKey = descendants ? identifier + ' ' : identifier;
8455
+ let pattern = this.pathPatterns.get(cacheKey);
8456
+ if (!pattern) {
8457
+ const source = identifier
8458
+ .split('[*]')
8459
+ .map(part => part.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
8460
+ .join('(?:\\[[^\\]]*\\]|\\.[^.[]+)');
8461
+ pattern = new RegExp(`^${source}${descendants ? '(?:[.[].*)?' : ''}$`);
8462
+ this.pathPatterns.set(cacheKey, pattern);
8463
+ }
8464
+ return pattern.test(changePath);
8465
+ }
8416
8466
  }
8417
8467
 
8418
8468
  // Copyright (c) 2025 MintJams Inc. Licensed under MIT License.
@@ -8908,18 +8958,15 @@
8908
8958
  * global aliases (computed properties etc.).
8909
8959
  */
8910
8960
  doesChangeMatchIdentifier(changePath, identifier) {
8911
- // Direct match
8912
- if (changePath === identifier) {
8961
+ // Direct match (an index "[*]" in the identifier matches any index)
8962
+ if (ReactiveProxy.pathMatches(changePath, identifier)) {
8913
8963
  return true;
8914
8964
  }
8915
8965
  // Resolve via local (scoped) alias chain
8916
8966
  const resolved = this.resolveAlias(identifier);
8917
8967
  if (resolved) {
8918
- if (changePath === resolved) {
8919
- return true;
8920
- }
8921
- // changePath is a descendant of resolved (e.g. "files[0].isModified" for resolved "files[0]")
8922
- if (changePath.startsWith(resolved + '.') || changePath.startsWith(resolved + '[')) {
8968
+ // changePath is resolved itself or a descendant of it (e.g. "files[0].isModified" for resolved "files[0]")
8969
+ if (ReactiveProxy.pathMatches(changePath, resolved, true)) {
8923
8970
  return true;
8924
8971
  }
8925
8972
  // changePath is an ancestor of resolved (e.g. "files" for resolved "files[0].isModified")
@@ -9984,7 +10031,7 @@
9984
10031
  // Build base parameter names (no dots), preserving order of first appearance
9985
10032
  const baseArgs = [];
9986
10033
  for (const id of ids) {
9987
- const base = id.split('.')[0];
10034
+ const base = ExpressionUtils.baseIdentifier(id);
9988
10035
  if (!baseArgs.includes(base)) {
9989
10036
  baseArgs.push(base);
9990
10037
  }
@@ -12060,6 +12107,11 @@
12060
12107
  * The expression evaluator for this directive.
12061
12108
  */
12062
12109
  #evaluator;
12110
+ /**
12111
+ * The HTML last set on the element. The same HTML is not set again: that
12112
+ * would replace the elements inside, and load their frames and images again.
12113
+ */
12114
+ #html;
12063
12115
  /**
12064
12116
  * @param context The context for parsing the directive.
12065
12117
  */
@@ -12110,10 +12162,14 @@
12110
12162
  get dependentIdentifiers() {
12111
12163
  return identifiers;
12112
12164
  },
12113
- applyToDOM() {
12165
+ applyToDOM: () => {
12114
12166
  const element = vNode.node;
12115
- const htmlContent = evaluator.evaluate();
12116
- element.innerHTML = htmlContent ?? '';
12167
+ const htmlContent = evaluator.evaluate() ?? '';
12168
+ if (htmlContent === this.#html) {
12169
+ return;
12170
+ }
12171
+ this.#html = htmlContent;
12172
+ element.innerHTML = htmlContent;
12117
12173
  }
12118
12174
  };
12119
12175
  return updater;
@@ -12320,7 +12376,7 @@
12320
12376
  const identifiers = optionsDirective.dependentIdentifiers;
12321
12377
  const baseArgs = [];
12322
12378
  for (const id of identifiers) {
12323
- const base = id.split('.')[0];
12379
+ const base = ExpressionUtils.baseIdentifier(id);
12324
12380
  if (!baseArgs.includes(base))
12325
12381
  baseArgs.push(base);
12326
12382
  }
@@ -12400,7 +12456,7 @@
12400
12456
  // Note: inline expressions receive entries and $ctx as parameters
12401
12457
  const baseArgs = [];
12402
12458
  for (const id of identifiers) {
12403
- const base = id.split('.')[0];
12459
+ const base = ExpressionUtils.baseIdentifier(id);
12404
12460
  if (!baseArgs.includes(base))
12405
12461
  baseArgs.push(base);
12406
12462
  }
@@ -13538,7 +13594,7 @@
13538
13594
  const identifiers = optionsDirective.dependentIdentifiers;
13539
13595
  const baseArgs = [];
13540
13596
  for (const id of identifiers) {
13541
- const base = id.split('.')[0];
13597
+ const base = ExpressionUtils.baseIdentifier(id);
13542
13598
  if (!baseArgs.includes(base))
13543
13599
  baseArgs.push(base);
13544
13600
  }
@@ -13626,7 +13682,7 @@
13626
13682
  // Note: inline expressions receive entries, observer, options, and $ctx as parameters
13627
13683
  const baseArgs = [];
13628
13684
  for (const id of identifiers) {
13629
- const base = id.split('.')[0];
13685
+ const base = ExpressionUtils.baseIdentifier(id);
13630
13686
  if (!baseArgs.includes(base))
13631
13687
  baseArgs.push(base);
13632
13688
  }
@@ -13759,7 +13815,7 @@
13759
13815
  const identifiers = optionsDirective.dependentIdentifiers;
13760
13816
  const baseArgs = [];
13761
13817
  for (const id of identifiers) {
13762
- const base = id.split('.')[0];
13818
+ const base = ExpressionUtils.baseIdentifier(id);
13763
13819
  if (!baseArgs.includes(base))
13764
13820
  baseArgs.push(base);
13765
13821
  }
@@ -13839,7 +13895,7 @@
13839
13895
  // Note: inline expressions receive entries and $ctx as parameters
13840
13896
  const baseArgs = [];
13841
13897
  for (const id of identifiers) {
13842
- const base = id.split('.')[0];
13898
+ const base = ExpressionUtils.baseIdentifier(id);
13843
13899
  if (!baseArgs.includes(base))
13844
13900
  baseArgs.push(base);
13845
13901
  }