@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.
package/dist/ichigo.cjs CHANGED
@@ -6865,11 +6865,16 @@
6865
6865
  const ast = parse(source, { ecmaVersion: "latest" });
6866
6866
  const dependencies = new Set();
6867
6867
  const declaredVariables = new Set();
6868
+ const thisAliases = new Set();
6868
6869
  // First, collect all declared variables (const, let, var, function params, etc.)
6869
6870
  ancestor(ast, {
6870
6871
  VariableDeclarator(node) {
6871
6872
  if (node.id.type === 'Identifier') {
6872
6873
  declaredVariables.add(node.id.name);
6874
+ // Detect aliases assigned from `this`, e.g. `let vm = this;`
6875
+ if (node.init && node.init.type === 'ThisExpression') {
6876
+ thisAliases.add(node.id.name);
6877
+ }
6873
6878
  }
6874
6879
  },
6875
6880
  FunctionDeclaration(node) {
@@ -6921,9 +6926,14 @@
6921
6926
  }
6922
6927
  },
6923
6928
  MemberExpression(node) {
6924
- // Handle 'this.propertyName' patterns
6925
- if (node.object.type === 'ThisExpression' && node.property.type === 'Identifier') {
6926
- dependencies.add(node.property.name);
6929
+ // Handle 'this.propertyName' patterns and aliases like 'vm.x' when 'vm = this'
6930
+ if (node.property.type === 'Identifier') {
6931
+ if (node.object.type === 'ThisExpression') {
6932
+ dependencies.add(node.property.name);
6933
+ }
6934
+ else if (node.object.type === 'Identifier' && thisAliases.has(node.object.name)) {
6935
+ dependencies.add(node.property.name);
6936
+ }
6927
6937
  }
6928
6938
  }
6929
6939
  });
@@ -7017,7 +7027,7 @@
7017
7027
  return expression;
7018
7028
  }
7019
7029
  try {
7020
- // Build a map of positions to replace: { start: number, end: number, name: string }[]
7030
+ // Build a map of positions to replace: { start: number, end: number, name: string, asThisAlias?: boolean }[]
7021
7031
  const replacements = [];
7022
7032
  // In script mode we must not wrap in parens (that would make multi-statement input invalid).
7023
7033
  // Offsets from the parser therefore refer directly to the original expression, so no shift.
@@ -7026,13 +7036,17 @@
7026
7036
  const offsetShift = asScript ? 0 : 1;
7027
7037
  const parsedAst = parse(source, { ecmaVersion: 'latest' });
7028
7038
  // Track identifiers that are locally declared within the handler body (let/const/var, function
7029
- // params) so we don't rewrite them to `this.xxx`. Only relevant in script mode, where the user
7030
- // can write declarations; in expression mode there are no declarations to track.
7039
+ // params) so we don't rewrite them to `this.xxx`. Additionally detect `this` aliases such as
7040
+ // `let vm = this;` so that `vm.x` can be rewritten to `this.x` even though `vm` is locally declared.
7031
7041
  const locallyDeclared = new Set();
7042
+ const thisAliases = new Set();
7032
7043
  if (asScript) {
7033
7044
  full(parsedAst, (node) => {
7034
7045
  if (node.type === 'VariableDeclarator' && node.id?.type === 'Identifier') {
7035
7046
  locallyDeclared.add(node.id.name);
7047
+ if (node.init && node.init.type === 'ThisExpression') {
7048
+ thisAliases.add(node.id.name);
7049
+ }
7036
7050
  }
7037
7051
  else if (node.type === 'FunctionDeclaration' && node.id?.type === 'Identifier') {
7038
7052
  locallyDeclared.add(node.id.name);
@@ -7049,20 +7063,34 @@
7049
7063
  if (!bindingIdentifiers.has(node.name)) {
7050
7064
  return;
7051
7065
  }
7052
- // Skip identifiers that were declared locally in the handler body
7066
+ // Locally declared identifiers must not be rewritten to `this.xxx`. The one
7067
+ // exception is a `this`-alias (e.g. `let vm = this;`): when such an alias is
7068
+ // used as the OBJECT of a MemberExpression (`vm.x`), we replace just the
7069
+ // object with `this` so that `vm.x` becomes `this.x`. In every other position
7070
+ // (the declaration LHS, a bare reference such as `console.log(vm)`, an
7071
+ // argument, etc.) the alias must be left untouched — rewriting it to
7072
+ // `this.vm` would either produce a syntax error (`let this.vm = this;`) or
7073
+ // change the meaning at runtime.
7074
+ // walk.fullAncestor includes the visited node itself as the last entry of
7075
+ // `ancestors`, so the actual parent node is at length - 2.
7076
+ const parentNode = ancestors.length >= 2 ? ancestors[ancestors.length - 2] : undefined;
7077
+ const isAliasAsMemberObject = parentNode?.type === 'MemberExpression' && parentNode.object === node;
7053
7078
  if (locallyDeclared.has(node.name)) {
7079
+ if (!thisAliases.has(node.name) || !isAliasAsMemberObject) {
7080
+ return;
7081
+ }
7082
+ replacements.push({
7083
+ start: node.start - offsetShift,
7084
+ end: node.end - offsetShift,
7085
+ name: node.name,
7086
+ asThisAlias: true
7087
+ });
7054
7088
  return;
7055
7089
  }
7056
- // Check if this identifier is a property of a MemberExpression
7057
- // (e.g., in 'obj.prop', we should skip 'prop')
7058
- if (ancestors.length >= 1) {
7059
- const parent = ancestors[ancestors.length - 1];
7060
- if (parent.type === 'MemberExpression') {
7061
- // Skip if this identifier is the property (not the object) of a non-computed member access
7062
- if (!parent.computed && parent.property === node) {
7063
- return;
7064
- }
7065
- }
7090
+ // Skip identifiers that are the property (not the object) of a non-computed
7091
+ // member access (e.g., the `prop` in `obj.prop`).
7092
+ if (parentNode?.type === 'MemberExpression' && !parentNode.computed && parentNode.property === node) {
7093
+ return;
7066
7094
  }
7067
7095
  // Add to replacements list (adjust for the wrapping parentheses in expression mode)
7068
7096
  replacements.push({
@@ -7076,9 +7104,8 @@
7076
7104
  // Apply replacements
7077
7105
  let result = expression;
7078
7106
  for (const replacement of replacements) {
7079
- result = result.substring(0, replacement.start) +
7080
- `this.${replacement.name}` +
7081
- result.substring(replacement.end);
7107
+ const insertion = replacement.asThisAlias ? 'this' : `this.${replacement.name}`;
7108
+ result = result.substring(0, replacement.start) + insertion + result.substring(replacement.end);
7082
7109
  }
7083
7110
  return result;
7084
7111
  }
@@ -11170,12 +11197,23 @@
11170
11197
  #collectDependentIdentifiers() {
11171
11198
  const ids = new Set(this.#evaluator?.dependentIdentifiers ?? []);
11172
11199
  const element = this.#vNode.node;
11173
- if (element instanceof HTMLInputElement && element.type === 'checkbox') {
11200
+ if (element instanceof HTMLInputElement) {
11174
11201
  const manager = this.#vNode.directiveManager;
11175
- for (const attrName of ['value', 'true-value', 'false-value']) {
11176
- const bindDirective = manager?.findBindDirective(attrName);
11177
- if (bindDirective) {
11178
- bindDirective.dependentIdentifiers.forEach(id => ids.add(id));
11202
+ // For checkboxes and radios, v-model's rendered state depends on
11203
+ // the element's typed `:value` binding (if present). Checkboxes
11204
+ // additionally depend on `:true-value` / `:false-value` bindings.
11205
+ if (element.type === 'checkbox' || element.type === 'radio') {
11206
+ const valueBind = manager?.findBindDirective('value');
11207
+ if (valueBind) {
11208
+ valueBind.dependentIdentifiers.forEach(id => ids.add(id));
11209
+ }
11210
+ }
11211
+ if (element.type === 'checkbox') {
11212
+ for (const attrName of ['true-value', 'false-value']) {
11213
+ const bindDirective = manager?.findBindDirective(attrName);
11214
+ if (bindDirective) {
11215
+ bindDirective.dependentIdentifiers.forEach(id => ids.add(id));
11216
+ }
11179
11217
  }
11180
11218
  }
11181
11219
  }
@@ -11262,11 +11300,13 @@
11262
11300
  this.#renderCheckbox(element, value);
11263
11301
  }
11264
11302
  else if (element.type === 'radio') {
11265
- // Prefer the original typed value stored by VBindDirective (:value binding)
11266
- // to avoid type coercion issues (e.g., boolean false vs string "false").
11267
- const radioValue = element._value !== undefined
11268
- ? element._value
11269
- : element.value;
11303
+ // Prefer the typed value from a sibling :value binding when present,
11304
+ // falling back to any stored `_value` or the raw string `value`.
11305
+ const manager = this.#vNode.directiveManager;
11306
+ const bindDirective = manager?.findBindDirective('value');
11307
+ const radioValue = bindDirective !== undefined
11308
+ ? bindDirective.evaluate()
11309
+ : (element._value !== undefined ? element._value : element.value);
11270
11310
  element.checked = radioValue === value;
11271
11311
  }
11272
11312
  else {
@@ -11295,11 +11335,13 @@
11295
11335
  newValue = this.#computeCheckboxNewValue(target);
11296
11336
  }
11297
11337
  else if (target.type === 'radio') {
11298
- // Prefer the original typed value stored by VBindDirective (:value binding)
11299
- // to preserve the type on write-back (e.g., boolean false, number 0).
11300
- newValue = target._value !== undefined
11301
- ? target._value
11302
- : target.value;
11338
+ // Prefer the typed value from a sibling :value binding when present,
11339
+ // falling back to any stored `_value` or the raw string `value`.
11340
+ const manager = this.#vNode.directiveManager;
11341
+ const bindDirective = manager?.findBindDirective('value');
11342
+ newValue = bindDirective !== undefined
11343
+ ? bindDirective.evaluate()
11344
+ : (target._value !== undefined ? target._value : target.value);
11303
11345
  }
11304
11346
  else {
11305
11347
  newValue = target.value;