@mintjamsinc/ichigojs 0.1.24 → 0.1.25

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
@@ -6433,6 +6433,20 @@
6433
6433
  })(node, state, override);
6434
6434
  }
6435
6435
 
6436
+ // A full walk triggers the callback on each node
6437
+ function full(node, callback, baseVisitor, state, override) {
6438
+ if (!baseVisitor) { baseVisitor = base; }
6439
+ var last
6440
+ ;(function c(node, st, override) {
6441
+ var type = override || node.type;
6442
+ baseVisitor[type](node, st, c);
6443
+ if (last !== node) {
6444
+ callback(node, st, type);
6445
+ last = node;
6446
+ }
6447
+ })(node, state, override);
6448
+ }
6449
+
6436
6450
  function skipThrough(node, st, c) { c(node, st); }
6437
6451
  function ignore(_node, _st, _c) {}
6438
6452
 
@@ -6704,8 +6718,9 @@
6704
6718
  static extractIdentifiers(expression, functionDependencies) {
6705
6719
  const identifiers = new Set();
6706
6720
  const ast = parse(`(${expression})`, { ecmaVersion: "latest" });
6707
- simple(ast, {
6708
- Identifier(node) {
6721
+ // Use walk.full instead of walk.simple to visit ALL nodes including assignment LHS
6722
+ full(ast, (node) => {
6723
+ if (node.type === 'Identifier') {
6709
6724
  identifiers.add(node.name);
6710
6725
  // Check if the identifier is a function name
6711
6726
  if (functionDependencies[node.name]) {
@@ -10269,13 +10284,12 @@
10269
10284
  // This allows the method to access the DOM element, VNode, and userData
10270
10285
  return originalMethod($ctx);
10271
10286
  }
10272
- // For inline expressions, evaluate normally with $ctx parameter
10273
- // Note: $ctx is a reserved variable name for lifecycle context
10274
- const values = identifiers.map(id => vNode.bindings?.get(id));
10275
- const args = [...identifiers, '$ctx'].join(", ");
10276
- const funcBody = `return (${expression});`;
10277
- const func = new Function(args, funcBody);
10278
- return func.call(bindings?.raw, ...values, $ctx);
10287
+ // For inline expressions, rewrite to use 'this' context
10288
+ // This allows assignments like "currentTab = 'shop'" to work correctly
10289
+ const rewrittenExpr = this.#rewriteExpression(expression, identifiers);
10290
+ const funcBody = `return (${rewrittenExpr});`;
10291
+ const func = new Function('$ctx', funcBody);
10292
+ return func.call(bindings?.raw, $ctx);
10279
10293
  };
10280
10294
  }
10281
10295
  /**
@@ -10303,15 +10317,97 @@
10303
10317
  // Pass event as first argument and $ctx as second argument
10304
10318
  return originalMethod(event, $ctx);
10305
10319
  }
10306
- // For inline expressions, evaluate normally
10307
- // Note: inline expressions receive event and $ctx as parameters
10308
- const values = identifiers.map(id => vNode.bindings?.get(id));
10309
- const args = [...identifiers, 'event', '$ctx'].join(", ");
10310
- const funcBody = `return (${expression});`;
10311
- const func = new Function(args, funcBody);
10312
- return func.call(bindings?.raw, ...values, event, $ctx);
10320
+ // For inline expressions, rewrite to use 'this' context
10321
+ // This allows assignments like "currentTab = 'shop'" to work correctly
10322
+ const rewrittenExpr = this.#rewriteExpression(expression, identifiers);
10323
+ const funcBody = `return (${rewrittenExpr});`;
10324
+ const func = new Function('event', '$ctx', funcBody);
10325
+ return func.call(bindings?.raw, event, $ctx);
10313
10326
  };
10314
10327
  }
10328
+ /**
10329
+ * Rewrites an expression to replace identifiers with 'this.identifier'.
10330
+ * This allows direct property access and assignment without using 'with' statement.
10331
+ * Uses AST parsing to accurately identify which identifiers to replace.
10332
+ * @param expression The original expression string.
10333
+ * @param identifiers The list of identifiers that are available in bindings.
10334
+ * @returns The rewritten expression.
10335
+ */
10336
+ #rewriteExpression(expression, identifiers) {
10337
+ // Reserved words and built-in objects that should not be prefixed with 'this.'
10338
+ const reserved = new Set([
10339
+ 'event', '$ctx',
10340
+ 'true', 'false', 'null', 'undefined', 'NaN', 'Infinity',
10341
+ 'Math', 'Date', 'String', 'Number', 'Boolean', 'Array', 'Object',
10342
+ 'JSON', 'console', 'window', 'document', 'navigator',
10343
+ 'parseInt', 'parseFloat', 'isNaN', 'isFinite',
10344
+ 'encodeURI', 'decodeURI', 'encodeURIComponent', 'decodeURIComponent'
10345
+ ]);
10346
+ // Extract ALL identifiers from the expression (including assignment left-hand side)
10347
+ // This is necessary because the passed 'identifiers' parameter only includes
10348
+ // identifiers that are used (right-hand side), not assigned to (left-hand side)
10349
+ let allIdentifiersInExpression;
10350
+ try {
10351
+ allIdentifiersInExpression = ExpressionUtils.extractIdentifiers(expression, {});
10352
+ }
10353
+ catch (error) {
10354
+ console.warn('[ichigo.js] Failed to extract identifiers from expression:', expression, error);
10355
+ return expression;
10356
+ }
10357
+ // Create a Set of identifiers available in bindings (from data, computed, methods)
10358
+ // We need to know which identifiers are valid binding properties
10359
+ const bindingIdentifiers = new Set(identifiers.filter(id => !reserved.has(id)));
10360
+ // For assignment expressions, we also need to include the left-hand side identifier
10361
+ // even if it's not in the tracking identifiers (because it's being assigned, not read)
10362
+ for (const id of allIdentifiersInExpression) {
10363
+ if (!reserved.has(id)) {
10364
+ bindingIdentifiers.add(id);
10365
+ }
10366
+ }
10367
+ if (bindingIdentifiers.size === 0) {
10368
+ return expression;
10369
+ }
10370
+ try {
10371
+ // Build a map of positions to replace: { start: number, end: number, name: string }[]
10372
+ const replacements = [];
10373
+ const parsedAst = parse(`(${expression})`, { ecmaVersion: 'latest' });
10374
+ // Collect all identifier nodes that should be replaced
10375
+ // Use walk.full to ensure we visit ALL nodes including assignment LHS
10376
+ full(parsedAst, (node) => {
10377
+ if (node.type !== 'Identifier') {
10378
+ return;
10379
+ }
10380
+ // Skip if not in our identifier set
10381
+ if (!bindingIdentifiers.has(node.name)) {
10382
+ return;
10383
+ }
10384
+ // Note: We cannot easily determine parent context with walk.full
10385
+ // So we'll include all identifiers and rely on position-based replacement
10386
+ // This is simpler and works correctly for inline expressions
10387
+ // Add to replacements list (adjust for the wrapping parentheses)
10388
+ replacements.push({
10389
+ start: node.start - 1,
10390
+ end: node.end - 1,
10391
+ name: node.name
10392
+ });
10393
+ });
10394
+ // Sort replacements by start position (descending) to replace from end to start
10395
+ replacements.sort((a, b) => b.start - a.start);
10396
+ // Apply replacements
10397
+ let result = expression;
10398
+ for (const replacement of replacements) {
10399
+ result = result.substring(0, replacement.start) +
10400
+ `this.${replacement.name}` +
10401
+ result.substring(replacement.end);
10402
+ }
10403
+ return result;
10404
+ }
10405
+ catch (error) {
10406
+ // If AST parsing fails, fall back to the original expression
10407
+ console.warn('Failed to rewrite expression:', expression, error);
10408
+ return expression;
10409
+ }
10410
+ }
10315
10411
  }
10316
10412
 
10317
10413
  // Copyright (c) 2025 MintJams Inc. Licensed under MIT License.