@mintjamsinc/ichigojs 0.1.65 → 0.1.66

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.
@@ -6859,11 +6859,16 @@ class ExpressionUtils {
6859
6859
  const ast = parse(source, { ecmaVersion: "latest" });
6860
6860
  const dependencies = new Set();
6861
6861
  const declaredVariables = new Set();
6862
+ const thisAliases = new Set();
6862
6863
  // First, collect all declared variables (const, let, var, function params, etc.)
6863
6864
  ancestor(ast, {
6864
6865
  VariableDeclarator(node) {
6865
6866
  if (node.id.type === 'Identifier') {
6866
6867
  declaredVariables.add(node.id.name);
6868
+ // Detect aliases assigned from `this`, e.g. `let vm = this;`
6869
+ if (node.init && node.init.type === 'ThisExpression') {
6870
+ thisAliases.add(node.id.name);
6871
+ }
6867
6872
  }
6868
6873
  },
6869
6874
  FunctionDeclaration(node) {
@@ -6915,9 +6920,14 @@ class ExpressionUtils {
6915
6920
  }
6916
6921
  },
6917
6922
  MemberExpression(node) {
6918
- // Handle 'this.propertyName' patterns
6919
- if (node.object.type === 'ThisExpression' && node.property.type === 'Identifier') {
6920
- dependencies.add(node.property.name);
6923
+ // Handle 'this.propertyName' patterns and aliases like 'vm.x' when 'vm = this'
6924
+ if (node.property.type === 'Identifier') {
6925
+ if (node.object.type === 'ThisExpression') {
6926
+ dependencies.add(node.property.name);
6927
+ }
6928
+ else if (node.object.type === 'Identifier' && thisAliases.has(node.object.name)) {
6929
+ dependencies.add(node.property.name);
6930
+ }
6921
6931
  }
6922
6932
  }
6923
6933
  });
@@ -7011,7 +7021,7 @@ class ExpressionUtils {
7011
7021
  return expression;
7012
7022
  }
7013
7023
  try {
7014
- // Build a map of positions to replace: { start: number, end: number, name: string }[]
7024
+ // Build a map of positions to replace: { start: number, end: number, name: string, asThisAlias?: boolean }[]
7015
7025
  const replacements = [];
7016
7026
  // In script mode we must not wrap in parens (that would make multi-statement input invalid).
7017
7027
  // Offsets from the parser therefore refer directly to the original expression, so no shift.
@@ -7020,13 +7030,17 @@ class ExpressionUtils {
7020
7030
  const offsetShift = asScript ? 0 : 1;
7021
7031
  const parsedAst = parse(source, { ecmaVersion: 'latest' });
7022
7032
  // Track identifiers that are locally declared within the handler body (let/const/var, function
7023
- // params) so we don't rewrite them to `this.xxx`. Only relevant in script mode, where the user
7024
- // can write declarations; in expression mode there are no declarations to track.
7033
+ // params) so we don't rewrite them to `this.xxx`. Additionally detect `this` aliases such as
7034
+ // `let vm = this;` so that `vm.x` can be rewritten to `this.x` even though `vm` is locally declared.
7025
7035
  const locallyDeclared = new Set();
7036
+ const thisAliases = new Set();
7026
7037
  if (asScript) {
7027
7038
  full(parsedAst, (node) => {
7028
7039
  if (node.type === 'VariableDeclarator' && node.id?.type === 'Identifier') {
7029
7040
  locallyDeclared.add(node.id.name);
7041
+ if (node.init && node.init.type === 'ThisExpression') {
7042
+ thisAliases.add(node.id.name);
7043
+ }
7030
7044
  }
7031
7045
  else if (node.type === 'FunctionDeclaration' && node.id?.type === 'Identifier') {
7032
7046
  locallyDeclared.add(node.id.name);
@@ -7043,20 +7057,34 @@ class ExpressionUtils {
7043
7057
  if (!bindingIdentifiers.has(node.name)) {
7044
7058
  return;
7045
7059
  }
7046
- // Skip identifiers that were declared locally in the handler body
7060
+ // Locally declared identifiers must not be rewritten to `this.xxx`. The one
7061
+ // exception is a `this`-alias (e.g. `let vm = this;`): when such an alias is
7062
+ // used as the OBJECT of a MemberExpression (`vm.x`), we replace just the
7063
+ // object with `this` so that `vm.x` becomes `this.x`. In every other position
7064
+ // (the declaration LHS, a bare reference such as `console.log(vm)`, an
7065
+ // argument, etc.) the alias must be left untouched — rewriting it to
7066
+ // `this.vm` would either produce a syntax error (`let this.vm = this;`) or
7067
+ // change the meaning at runtime.
7068
+ // walk.fullAncestor includes the visited node itself as the last entry of
7069
+ // `ancestors`, so the actual parent node is at length - 2.
7070
+ const parentNode = ancestors.length >= 2 ? ancestors[ancestors.length - 2] : undefined;
7071
+ const isAliasAsMemberObject = parentNode?.type === 'MemberExpression' && parentNode.object === node;
7047
7072
  if (locallyDeclared.has(node.name)) {
7073
+ if (!thisAliases.has(node.name) || !isAliasAsMemberObject) {
7074
+ return;
7075
+ }
7076
+ replacements.push({
7077
+ start: node.start - offsetShift,
7078
+ end: node.end - offsetShift,
7079
+ name: node.name,
7080
+ asThisAlias: true
7081
+ });
7048
7082
  return;
7049
7083
  }
7050
- // Check if this identifier is a property of a MemberExpression
7051
- // (e.g., in 'obj.prop', we should skip 'prop')
7052
- if (ancestors.length >= 1) {
7053
- const parent = ancestors[ancestors.length - 1];
7054
- if (parent.type === 'MemberExpression') {
7055
- // Skip if this identifier is the property (not the object) of a non-computed member access
7056
- if (!parent.computed && parent.property === node) {
7057
- return;
7058
- }
7059
- }
7084
+ // Skip identifiers that are the property (not the object) of a non-computed
7085
+ // member access (e.g., the `prop` in `obj.prop`).
7086
+ if (parentNode?.type === 'MemberExpression' && !parentNode.computed && parentNode.property === node) {
7087
+ return;
7060
7088
  }
7061
7089
  // Add to replacements list (adjust for the wrapping parentheses in expression mode)
7062
7090
  replacements.push({
@@ -7070,9 +7098,8 @@ class ExpressionUtils {
7070
7098
  // Apply replacements
7071
7099
  let result = expression;
7072
7100
  for (const replacement of replacements) {
7073
- result = result.substring(0, replacement.start) +
7074
- `this.${replacement.name}` +
7075
- result.substring(replacement.end);
7101
+ const insertion = replacement.asThisAlias ? 'this' : `this.${replacement.name}`;
7102
+ result = result.substring(0, replacement.start) + insertion + result.substring(replacement.end);
7076
7103
  }
7077
7104
  return result;
7078
7105
  }
@@ -11164,12 +11191,23 @@ class VModelDirective {
11164
11191
  #collectDependentIdentifiers() {
11165
11192
  const ids = new Set(this.#evaluator?.dependentIdentifiers ?? []);
11166
11193
  const element = this.#vNode.node;
11167
- if (element instanceof HTMLInputElement && element.type === 'checkbox') {
11194
+ if (element instanceof HTMLInputElement) {
11168
11195
  const manager = this.#vNode.directiveManager;
11169
- for (const attrName of ['value', 'true-value', 'false-value']) {
11170
- const bindDirective = manager?.findBindDirective(attrName);
11171
- if (bindDirective) {
11172
- bindDirective.dependentIdentifiers.forEach(id => ids.add(id));
11196
+ // For checkboxes and radios, v-model's rendered state depends on
11197
+ // the element's typed `:value` binding (if present). Checkboxes
11198
+ // additionally depend on `:true-value` / `:false-value` bindings.
11199
+ if (element.type === 'checkbox' || element.type === 'radio') {
11200
+ const valueBind = manager?.findBindDirective('value');
11201
+ if (valueBind) {
11202
+ valueBind.dependentIdentifiers.forEach(id => ids.add(id));
11203
+ }
11204
+ }
11205
+ if (element.type === 'checkbox') {
11206
+ for (const attrName of ['true-value', 'false-value']) {
11207
+ const bindDirective = manager?.findBindDirective(attrName);
11208
+ if (bindDirective) {
11209
+ bindDirective.dependentIdentifiers.forEach(id => ids.add(id));
11210
+ }
11173
11211
  }
11174
11212
  }
11175
11213
  }
@@ -11256,11 +11294,13 @@ class VModelDirective {
11256
11294
  this.#renderCheckbox(element, value);
11257
11295
  }
11258
11296
  else if (element.type === 'radio') {
11259
- // Prefer the original typed value stored by VBindDirective (:value binding)
11260
- // to avoid type coercion issues (e.g., boolean false vs string "false").
11261
- const radioValue = element._value !== undefined
11262
- ? element._value
11263
- : element.value;
11297
+ // Prefer the typed value from a sibling :value binding when present,
11298
+ // falling back to any stored `_value` or the raw string `value`.
11299
+ const manager = this.#vNode.directiveManager;
11300
+ const bindDirective = manager?.findBindDirective('value');
11301
+ const radioValue = bindDirective !== undefined
11302
+ ? bindDirective.evaluate()
11303
+ : (element._value !== undefined ? element._value : element.value);
11264
11304
  element.checked = radioValue === value;
11265
11305
  }
11266
11306
  else {
@@ -11289,11 +11329,13 @@ class VModelDirective {
11289
11329
  newValue = this.#computeCheckboxNewValue(target);
11290
11330
  }
11291
11331
  else if (target.type === 'radio') {
11292
- // Prefer the original typed value stored by VBindDirective (:value binding)
11293
- // to preserve the type on write-back (e.g., boolean false, number 0).
11294
- newValue = target._value !== undefined
11295
- ? target._value
11296
- : target.value;
11332
+ // Prefer the typed value from a sibling :value binding when present,
11333
+ // falling back to any stored `_value` or the raw string `value`.
11334
+ const manager = this.#vNode.directiveManager;
11335
+ const bindDirective = manager?.findBindDirective('value');
11336
+ newValue = bindDirective !== undefined
11337
+ ? bindDirective.evaluate()
11338
+ : (target._value !== undefined ? target._value : target.value);
11297
11339
  }
11298
11340
  else {
11299
11341
  newValue = target.value;