@mintjamsinc/ichigojs 0.1.64 → 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 +226 -39
- package/dist/ichigo.cjs.map +1 -1
- package/dist/ichigo.esm.js +226 -39
- package/dist/ichigo.esm.js.map +1 -1
- package/dist/ichigo.esm.min.js +1 -1
- package/dist/ichigo.min.cjs +1 -1
- package/dist/ichigo.umd.js +226 -39
- package/dist/ichigo.umd.js.map +1 -1
- package/dist/ichigo.umd.min.js +1 -1
- package/dist/types/ichigo/directives/VBindDirective.d.ts +5 -0
- package/dist/types/ichigo/directives/VDirectiveManager.d.ts +9 -0
- package/package.json +1 -1
package/dist/ichigo.umd.js
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.
|
|
6926
|
-
|
|
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`.
|
|
7030
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
7057
|
-
// (e.g., in
|
|
7058
|
-
if (
|
|
7059
|
-
|
|
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
|
-
|
|
7080
|
-
|
|
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
|
}
|
|
@@ -7443,6 +7470,13 @@
|
|
|
7443
7470
|
get expression() {
|
|
7444
7471
|
return this.#expression;
|
|
7445
7472
|
}
|
|
7473
|
+
/**
|
|
7474
|
+
* Evaluates the bound expression and returns the current value.
|
|
7475
|
+
* Returns undefined when no evaluator is available (e.g., empty expression).
|
|
7476
|
+
*/
|
|
7477
|
+
evaluate() {
|
|
7478
|
+
return this.#evaluator?.evaluate();
|
|
7479
|
+
}
|
|
7446
7480
|
/**
|
|
7447
7481
|
* @inheritdoc
|
|
7448
7482
|
*/
|
|
@@ -8655,6 +8689,29 @@
|
|
|
8655
8689
|
get keyDirective() {
|
|
8656
8690
|
return this.#keyDirective;
|
|
8657
8691
|
}
|
|
8692
|
+
/**
|
|
8693
|
+
* Finds a VBindDirective that binds the given attribute name (e.g. "value",
|
|
8694
|
+
* "true-value", "false-value"). Returns undefined if no matching v-bind
|
|
8695
|
+
* directive is registered on this node.
|
|
8696
|
+
*
|
|
8697
|
+
* Used by directives such as v-model that need to read the typed value of a
|
|
8698
|
+
* sibling v-bind without depending on the attribute order in the source HTML.
|
|
8699
|
+
*/
|
|
8700
|
+
findBindDirective(attrName) {
|
|
8701
|
+
if (!this.#directives || this.#directives.length === 0) {
|
|
8702
|
+
return undefined;
|
|
8703
|
+
}
|
|
8704
|
+
for (const directive of this.#directives) {
|
|
8705
|
+
if (directive.name !== StandardDirectiveName.V_BIND) {
|
|
8706
|
+
continue;
|
|
8707
|
+
}
|
|
8708
|
+
const bindDirective = directive;
|
|
8709
|
+
if (bindDirective.attributeName === attrName) {
|
|
8710
|
+
return bindDirective;
|
|
8711
|
+
}
|
|
8712
|
+
}
|
|
8713
|
+
return undefined;
|
|
8714
|
+
}
|
|
8658
8715
|
/**
|
|
8659
8716
|
* Gets the VBindDirective for options specific to the given directive name.
|
|
8660
8717
|
* Searches in order: `:options.{directive}` -> `:options`
|
|
@@ -11108,14 +11165,13 @@
|
|
|
11108
11165
|
* @inheritdoc
|
|
11109
11166
|
*/
|
|
11110
11167
|
get domUpdater() {
|
|
11111
|
-
const
|
|
11112
|
-
// Create and return the DOM updater
|
|
11168
|
+
const self = this;
|
|
11113
11169
|
const updater = {
|
|
11114
11170
|
get dependentIdentifiers() {
|
|
11115
|
-
return
|
|
11171
|
+
return self.#collectDependentIdentifiers();
|
|
11116
11172
|
},
|
|
11117
11173
|
applyToDOM: () => {
|
|
11118
|
-
|
|
11174
|
+
self.#render();
|
|
11119
11175
|
}
|
|
11120
11176
|
};
|
|
11121
11177
|
return updater;
|
|
@@ -11130,7 +11186,38 @@
|
|
|
11130
11186
|
* @inheritdoc
|
|
11131
11187
|
*/
|
|
11132
11188
|
get dependentIdentifiers() {
|
|
11133
|
-
return this.#
|
|
11189
|
+
return this.#collectDependentIdentifiers();
|
|
11190
|
+
}
|
|
11191
|
+
/**
|
|
11192
|
+
* Collects identifiers this directive's render depends on. For checkboxes
|
|
11193
|
+
* this includes the v-model expression itself plus the expressions bound to
|
|
11194
|
+
* `:value`, `:true-value`, and `:false-value`, since the rendered checked
|
|
11195
|
+
* state changes when any of these change.
|
|
11196
|
+
*/
|
|
11197
|
+
#collectDependentIdentifiers() {
|
|
11198
|
+
const ids = new Set(this.#evaluator?.dependentIdentifiers ?? []);
|
|
11199
|
+
const element = this.#vNode.node;
|
|
11200
|
+
if (element instanceof HTMLInputElement) {
|
|
11201
|
+
const manager = this.#vNode.directiveManager;
|
|
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
|
+
}
|
|
11217
|
+
}
|
|
11218
|
+
}
|
|
11219
|
+
}
|
|
11220
|
+
return Array.from(ids);
|
|
11134
11221
|
}
|
|
11135
11222
|
/**
|
|
11136
11223
|
* @inheritdoc
|
|
@@ -11210,14 +11297,16 @@
|
|
|
11210
11297
|
// Update the element based on its type
|
|
11211
11298
|
if (element instanceof HTMLInputElement) {
|
|
11212
11299
|
if (element.type === 'checkbox') {
|
|
11213
|
-
element
|
|
11300
|
+
this.#renderCheckbox(element, value);
|
|
11214
11301
|
}
|
|
11215
11302
|
else if (element.type === 'radio') {
|
|
11216
|
-
// Prefer the
|
|
11217
|
-
//
|
|
11218
|
-
const
|
|
11219
|
-
|
|
11220
|
-
|
|
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);
|
|
11221
11310
|
element.checked = radioValue === value;
|
|
11222
11311
|
}
|
|
11223
11312
|
else {
|
|
@@ -11243,14 +11332,16 @@
|
|
|
11243
11332
|
// Get the new value based on element type
|
|
11244
11333
|
if (target instanceof HTMLInputElement) {
|
|
11245
11334
|
if (target.type === 'checkbox') {
|
|
11246
|
-
newValue = target
|
|
11335
|
+
newValue = this.#computeCheckboxNewValue(target);
|
|
11247
11336
|
}
|
|
11248
11337
|
else if (target.type === 'radio') {
|
|
11249
|
-
// Prefer the
|
|
11250
|
-
//
|
|
11251
|
-
|
|
11252
|
-
|
|
11253
|
-
|
|
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);
|
|
11254
11345
|
}
|
|
11255
11346
|
else {
|
|
11256
11347
|
newValue = target.value;
|
|
@@ -11259,13 +11350,109 @@
|
|
|
11259
11350
|
else if (target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement) {
|
|
11260
11351
|
newValue = target.value;
|
|
11261
11352
|
}
|
|
11262
|
-
// Apply modifiers to the value
|
|
11263
|
-
|
|
11353
|
+
// Apply modifiers to the value (skip for checkboxes: their value
|
|
11354
|
+
// is either boolean, a custom true/false value, or an array, none
|
|
11355
|
+
// of which should be coerced by .trim or .number).
|
|
11356
|
+
const isCheckbox = target instanceof HTMLInputElement && target.type === 'checkbox';
|
|
11357
|
+
if (!isCheckbox) {
|
|
11358
|
+
newValue = this.#applyModifiers(newValue);
|
|
11359
|
+
}
|
|
11264
11360
|
// Update the binding
|
|
11265
11361
|
this.#updateBinding(newValue);
|
|
11266
11362
|
};
|
|
11267
11363
|
element.addEventListener(eventName, this.#listener);
|
|
11268
11364
|
}
|
|
11365
|
+
/**
|
|
11366
|
+
* Renders a checkbox in one of three modes (Vue-compatible):
|
|
11367
|
+
* 1. Array binding: the bound value is an array; the checkbox is checked
|
|
11368
|
+
* when its element-value is a member of that array.
|
|
11369
|
+
* 2. true-value/false-value binding: when `:true-value` (and optionally
|
|
11370
|
+
* `:false-value`) is provided via v-bind, the checkbox is checked
|
|
11371
|
+
* when the bound value strictly equals the resolved true-value.
|
|
11372
|
+
* 3. Boolean binding (default): the bound value is coerced to boolean.
|
|
11373
|
+
*/
|
|
11374
|
+
#renderCheckbox(element, value) {
|
|
11375
|
+
if (Array.isArray(value)) {
|
|
11376
|
+
const elementValue = this.#resolveCheckboxElementValue(element);
|
|
11377
|
+
element.checked = value.indexOf(elementValue) !== -1;
|
|
11378
|
+
return;
|
|
11379
|
+
}
|
|
11380
|
+
const trueValueDescriptor = this.#resolveCheckboxTrueFalseValues(element);
|
|
11381
|
+
if (trueValueDescriptor) {
|
|
11382
|
+
element.checked = value === trueValueDescriptor.trueValue;
|
|
11383
|
+
return;
|
|
11384
|
+
}
|
|
11385
|
+
element.checked = !!value;
|
|
11386
|
+
}
|
|
11387
|
+
/**
|
|
11388
|
+
* Computes the value to write back to the bound expression when a checkbox
|
|
11389
|
+
* change event fires. Mirrors the three-mode logic of #renderCheckbox.
|
|
11390
|
+
*
|
|
11391
|
+
* For array binding, the current value of the bound expression is read so
|
|
11392
|
+
* that a fresh array can be returned (the existing array is not mutated,
|
|
11393
|
+
* which preserves reactivity semantics).
|
|
11394
|
+
*/
|
|
11395
|
+
#computeCheckboxNewValue(target) {
|
|
11396
|
+
const currentValue = this.#evaluator?.evaluate();
|
|
11397
|
+
if (Array.isArray(currentValue)) {
|
|
11398
|
+
const elementValue = this.#resolveCheckboxElementValue(target);
|
|
11399
|
+
const next = currentValue.slice();
|
|
11400
|
+
const index = next.indexOf(elementValue);
|
|
11401
|
+
if (target.checked) {
|
|
11402
|
+
if (index === -1) {
|
|
11403
|
+
next.push(elementValue);
|
|
11404
|
+
}
|
|
11405
|
+
}
|
|
11406
|
+
else {
|
|
11407
|
+
if (index !== -1) {
|
|
11408
|
+
next.splice(index, 1);
|
|
11409
|
+
}
|
|
11410
|
+
}
|
|
11411
|
+
return next;
|
|
11412
|
+
}
|
|
11413
|
+
const trueValueDescriptor = this.#resolveCheckboxTrueFalseValues(target);
|
|
11414
|
+
if (trueValueDescriptor) {
|
|
11415
|
+
return target.checked ? trueValueDescriptor.trueValue : trueValueDescriptor.falseValue;
|
|
11416
|
+
}
|
|
11417
|
+
return target.checked;
|
|
11418
|
+
}
|
|
11419
|
+
/**
|
|
11420
|
+
* Resolves the typed element value for a checkbox. Prefers the value bound
|
|
11421
|
+
* via `:value` (evaluated through the sibling VBindDirective so type is
|
|
11422
|
+
* preserved), then the typed value previously stored on the element by
|
|
11423
|
+
* VBindDirective, and finally the raw string `value` attribute.
|
|
11424
|
+
*/
|
|
11425
|
+
#resolveCheckboxElementValue(element) {
|
|
11426
|
+
const bindDirective = this.#vNode.directiveManager?.findBindDirective('value');
|
|
11427
|
+
if (bindDirective) {
|
|
11428
|
+
return bindDirective.evaluate();
|
|
11429
|
+
}
|
|
11430
|
+
if (element._value !== undefined) {
|
|
11431
|
+
return element._value;
|
|
11432
|
+
}
|
|
11433
|
+
return element.value;
|
|
11434
|
+
}
|
|
11435
|
+
/**
|
|
11436
|
+
* Resolves the (true-value, false-value) pair for a checkbox if either is
|
|
11437
|
+
* bound via `:true-value` or `:false-value`. Returns undefined when no
|
|
11438
|
+
* true/false value binding is present, signalling that the default boolean
|
|
11439
|
+
* mode should be used.
|
|
11440
|
+
*
|
|
11441
|
+
* If only one of the two is bound, the other defaults match Vue: an unbound
|
|
11442
|
+
* true-value defaults to literal `true`, an unbound false-value to `false`.
|
|
11443
|
+
*/
|
|
11444
|
+
#resolveCheckboxTrueFalseValues(element) {
|
|
11445
|
+
const manager = this.#vNode.directiveManager;
|
|
11446
|
+
const trueBind = manager?.findBindDirective('true-value');
|
|
11447
|
+
const falseBind = manager?.findBindDirective('false-value');
|
|
11448
|
+
if (!trueBind && !falseBind) {
|
|
11449
|
+
return undefined;
|
|
11450
|
+
}
|
|
11451
|
+
return {
|
|
11452
|
+
trueValue: trueBind ? trueBind.evaluate() : true,
|
|
11453
|
+
falseValue: falseBind ? falseBind.evaluate() : false,
|
|
11454
|
+
};
|
|
11455
|
+
}
|
|
11269
11456
|
/**
|
|
11270
11457
|
* Applies modifiers to the input value.
|
|
11271
11458
|
* @param value The value to process.
|