@mintjamsinc/ichigojs 0.1.55 → 0.1.57
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 +78 -38
- package/dist/ichigo.cjs.map +1 -1
- package/dist/ichigo.esm.js +78 -38
- 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 +78 -38
- package/dist/ichigo.umd.js.map +1 -1
- package/dist/ichigo.umd.min.js +1 -1
- package/dist/types/ichigo/util/ExpressionUtils.d.ts +10 -2
- package/package.json +1 -1
package/dist/ichigo.cjs
CHANGED
|
@@ -6737,11 +6737,18 @@
|
|
|
6737
6737
|
* Extracts variable and function names used in the expression.
|
|
6738
6738
|
* @param expression The expression string to analyze.
|
|
6739
6739
|
* @param functionDependencies A dictionary mapping function names to their dependencies.
|
|
6740
|
+
* @param options Optional parsing options.
|
|
6741
|
+
* - asScript: If true, parse the input as a Script (allows multi-statement source with semicolons,
|
|
6742
|
+
* declarations, control-flow, etc.). If false/omitted, parse as a single expression (the default,
|
|
6743
|
+
* for backward compatibility with interpolation and binding directives).
|
|
6740
6744
|
* @returns An array of identifier names.
|
|
6741
6745
|
*/
|
|
6742
|
-
static extractIdentifiers(expression, functionDependencies) {
|
|
6746
|
+
static extractIdentifiers(expression, functionDependencies, options) {
|
|
6743
6747
|
const identifiers = new Set();
|
|
6744
|
-
|
|
6748
|
+
// In expression mode we wrap in parens so acorn parses the source as a single expression.
|
|
6749
|
+
// In script mode we parse as a Program so that multi-statement bodies (e.g. "a=1; b=2") work.
|
|
6750
|
+
const source = options?.asScript ? expression : `(${expression})`;
|
|
6751
|
+
const ast = parse(source, { ecmaVersion: "latest" });
|
|
6745
6752
|
// Use walk.full instead of walk.simple to visit ALL nodes including assignment LHS
|
|
6746
6753
|
full(ast, (node) => {
|
|
6747
6754
|
if (node.type === 'Identifier') {
|
|
@@ -6973,10 +6980,10 @@
|
|
|
6973
6980
|
* @param identifiers The list of identifiers that are available in bindings.
|
|
6974
6981
|
* @returns The rewritten expression.
|
|
6975
6982
|
*/
|
|
6976
|
-
static rewriteExpression(expression, identifiers) {
|
|
6983
|
+
static rewriteExpression(expression, identifiers, options) {
|
|
6977
6984
|
// Reserved words and built-in objects that should not be prefixed with 'this.'
|
|
6978
6985
|
const reserved = new Set([
|
|
6979
|
-
'event', '$ctx', '$newValue',
|
|
6986
|
+
'event', '$event', '$ctx', '$newValue',
|
|
6980
6987
|
'true', 'false', 'null', 'undefined', 'NaN', 'Infinity',
|
|
6981
6988
|
'Math', 'Date', 'String', 'Number', 'Boolean', 'Array', 'Object',
|
|
6982
6989
|
'JSON', 'console', 'window', 'document', 'navigator',
|
|
@@ -6988,7 +6995,7 @@
|
|
|
6988
6995
|
// identifiers that are used (right-hand side), not assigned to (left-hand side)
|
|
6989
6996
|
let allIdentifiersInExpression;
|
|
6990
6997
|
try {
|
|
6991
|
-
allIdentifiersInExpression = ExpressionUtils.extractIdentifiers(expression, {});
|
|
6998
|
+
allIdentifiersInExpression = ExpressionUtils.extractIdentifiers(expression, {}, options);
|
|
6992
6999
|
}
|
|
6993
7000
|
catch (error) {
|
|
6994
7001
|
console.warn('[ichigo.js] Failed to extract identifiers from expression:', expression, error);
|
|
@@ -7010,7 +7017,26 @@
|
|
|
7010
7017
|
try {
|
|
7011
7018
|
// Build a map of positions to replace: { start: number, end: number, name: string }[]
|
|
7012
7019
|
const replacements = [];
|
|
7013
|
-
|
|
7020
|
+
// In script mode we must not wrap in parens (that would make multi-statement input invalid).
|
|
7021
|
+
// Offsets from the parser therefore refer directly to the original expression, so no shift.
|
|
7022
|
+
const asScript = options?.asScript === true;
|
|
7023
|
+
const source = asScript ? expression : `(${expression})`;
|
|
7024
|
+
const offsetShift = asScript ? 0 : 1;
|
|
7025
|
+
const parsedAst = parse(source, { ecmaVersion: 'latest' });
|
|
7026
|
+
// Track identifiers that are locally declared within the handler body (let/const/var, function
|
|
7027
|
+
// params) so we don't rewrite them to `this.xxx`. Only relevant in script mode, where the user
|
|
7028
|
+
// can write declarations; in expression mode there are no declarations to track.
|
|
7029
|
+
const locallyDeclared = new Set();
|
|
7030
|
+
if (asScript) {
|
|
7031
|
+
full(parsedAst, (node) => {
|
|
7032
|
+
if (node.type === 'VariableDeclarator' && node.id?.type === 'Identifier') {
|
|
7033
|
+
locallyDeclared.add(node.id.name);
|
|
7034
|
+
}
|
|
7035
|
+
else if (node.type === 'FunctionDeclaration' && node.id?.type === 'Identifier') {
|
|
7036
|
+
locallyDeclared.add(node.id.name);
|
|
7037
|
+
}
|
|
7038
|
+
});
|
|
7039
|
+
}
|
|
7014
7040
|
// Collect all identifier nodes that should be replaced
|
|
7015
7041
|
// Use walk.fullAncestor to visit ALL nodes (including assignment LHS) while tracking ancestors
|
|
7016
7042
|
fullAncestor(parsedAst, (node, _state, ancestors) => {
|
|
@@ -7021,6 +7047,10 @@
|
|
|
7021
7047
|
if (!bindingIdentifiers.has(node.name)) {
|
|
7022
7048
|
return;
|
|
7023
7049
|
}
|
|
7050
|
+
// Skip identifiers that were declared locally in the handler body
|
|
7051
|
+
if (locallyDeclared.has(node.name)) {
|
|
7052
|
+
return;
|
|
7053
|
+
}
|
|
7024
7054
|
// Check if this identifier is a property of a MemberExpression
|
|
7025
7055
|
// (e.g., in 'obj.prop', we should skip 'prop')
|
|
7026
7056
|
if (ancestors.length >= 1) {
|
|
@@ -7032,10 +7062,10 @@
|
|
|
7032
7062
|
}
|
|
7033
7063
|
}
|
|
7034
7064
|
}
|
|
7035
|
-
// Add to replacements list (adjust for the wrapping parentheses)
|
|
7065
|
+
// Add to replacements list (adjust for the wrapping parentheses in expression mode)
|
|
7036
7066
|
replacements.push({
|
|
7037
|
-
start: node.start -
|
|
7038
|
-
end: node.end -
|
|
7067
|
+
start: node.start - offsetShift,
|
|
7068
|
+
end: node.end - offsetShift,
|
|
7039
7069
|
name: node.name
|
|
7040
7070
|
});
|
|
7041
7071
|
});
|
|
@@ -11234,10 +11264,12 @@
|
|
|
11234
11264
|
this.#eventName = parts[0];
|
|
11235
11265
|
parts.slice(1).forEach(mod => this.#modifiers.add(mod));
|
|
11236
11266
|
}
|
|
11237
|
-
// Parse the expression to extract identifiers and create the handler wrapper
|
|
11267
|
+
// Parse the expression to extract identifiers and create the handler wrapper.
|
|
11268
|
+
// Event handlers are parsed in script mode so that users can write multi-statement bodies
|
|
11269
|
+
// (e.g. "a=1; b=2"), declarations, and control-flow constructs — matching Vue semantics.
|
|
11238
11270
|
const expression = context.attribute.value;
|
|
11239
11271
|
if (expression) {
|
|
11240
|
-
this.#dependentIdentifiers = ExpressionUtils.extractIdentifiers(expression, context.vNode.vApplication.functionDependencies);
|
|
11272
|
+
this.#dependentIdentifiers = ExpressionUtils.extractIdentifiers(expression, context.vNode.vApplication.functionDependencies, { asScript: true });
|
|
11241
11273
|
}
|
|
11242
11274
|
// Check if this is a lifecycle hook or a regular event
|
|
11243
11275
|
if (this.#eventName && this.#isLifecycleHook(this.#eventName)) {
|
|
@@ -11367,23 +11399,27 @@
|
|
|
11367
11399
|
this.#listener = (event) => {
|
|
11368
11400
|
// Check key modifiers for keyboard events
|
|
11369
11401
|
if (event instanceof KeyboardEvent) {
|
|
11370
|
-
|
|
11371
|
-
|
|
11402
|
+
// Map of modifier alias -> KeyboardEvent.key values it matches.
|
|
11403
|
+
// Multiple values allow a single modifier to match several physical keys
|
|
11404
|
+
// (e.g. `.delete` matches both Delete and Backspace, matching Vue's behavior).
|
|
11405
|
+
// Multiple aliases pointing to the same key are allowed (e.g. `.esc` / `.escape`).
|
|
11406
|
+
const keyMap = {
|
|
11407
|
+
'enter': ['Enter'],
|
|
11408
|
+
'tab': ['Tab'],
|
|
11409
|
+
'delete': ['Delete', 'Backspace'],
|
|
11410
|
+
'esc': ['Escape'],
|
|
11411
|
+
'escape': ['Escape'],
|
|
11412
|
+
'space': [' '],
|
|
11413
|
+
'up': ['ArrowUp'],
|
|
11414
|
+
'down': ['ArrowDown'],
|
|
11415
|
+
'left': ['ArrowLeft'],
|
|
11416
|
+
'right': ['ArrowRight']
|
|
11417
|
+
};
|
|
11418
|
+
const hasKeyModifier = Object.keys(keyMap).some(key => this.#modifiers.has(key));
|
|
11372
11419
|
if (hasKeyModifier) {
|
|
11373
|
-
const keyMap = {
|
|
11374
|
-
'enter': 'Enter',
|
|
11375
|
-
'tab': 'Tab',
|
|
11376
|
-
'delete': 'Delete',
|
|
11377
|
-
'esc': 'Escape',
|
|
11378
|
-
'space': ' ',
|
|
11379
|
-
'up': 'ArrowUp',
|
|
11380
|
-
'down': 'ArrowDown',
|
|
11381
|
-
'left': 'ArrowLeft',
|
|
11382
|
-
'right': 'ArrowRight'
|
|
11383
|
-
};
|
|
11384
11420
|
let keyMatched = false;
|
|
11385
|
-
for (const [modifier,
|
|
11386
|
-
if (this.#modifiers.has(modifier) && event.key
|
|
11421
|
+
for (const [modifier, keyValues] of Object.entries(keyMap)) {
|
|
11422
|
+
if (this.#modifiers.has(modifier) && keyValues.includes(event.key)) {
|
|
11387
11423
|
keyMatched = true;
|
|
11388
11424
|
break;
|
|
11389
11425
|
}
|
|
@@ -11451,10 +11487,11 @@
|
|
|
11451
11487
|
// This allows the method to access the DOM element, VNode, and userData
|
|
11452
11488
|
return originalMethod($ctx);
|
|
11453
11489
|
}
|
|
11454
|
-
// For inline
|
|
11455
|
-
// This allows assignments like "currentTab = 'shop'" to work correctly
|
|
11456
|
-
|
|
11457
|
-
const
|
|
11490
|
+
// For inline bodies, rewrite to use 'this' context
|
|
11491
|
+
// This allows assignments like "currentTab = 'shop'" to work correctly.
|
|
11492
|
+
// Script mode allows multi-statement bodies (e.g. "a=1; init()") and control-flow.
|
|
11493
|
+
const rewrittenExpr = this.#rewriteExpression(expression, identifiers, { asScript: true });
|
|
11494
|
+
const funcBody = rewrittenExpr;
|
|
11458
11495
|
const func = new Function('$ctx', funcBody);
|
|
11459
11496
|
return func.call(bindings?.raw, $ctx);
|
|
11460
11497
|
};
|
|
@@ -11484,12 +11521,15 @@
|
|
|
11484
11521
|
// Pass event as first argument and $ctx as second argument
|
|
11485
11522
|
return originalMethod(event, $ctx);
|
|
11486
11523
|
}
|
|
11487
|
-
// For inline
|
|
11488
|
-
// This allows assignments like "currentTab = 'shop'" to work correctly
|
|
11489
|
-
|
|
11490
|
-
|
|
11491
|
-
const
|
|
11492
|
-
|
|
11524
|
+
// For inline bodies, rewrite to use 'this' context
|
|
11525
|
+
// This allows assignments like "currentTab = 'shop'" to work correctly.
|
|
11526
|
+
// Script mode allows multi-statement bodies (e.g. "a=1; b=2") and control-flow,
|
|
11527
|
+
// so we emit the rewritten source directly as the function body (no `return (...)`).
|
|
11528
|
+
const rewrittenExpr = this.#rewriteExpression(expression, identifiers, { asScript: true });
|
|
11529
|
+
const funcBody = rewrittenExpr;
|
|
11530
|
+
// '$event' is an alias for 'event' for Vue compatibility
|
|
11531
|
+
const func = new Function('event', '$event', '$ctx', funcBody);
|
|
11532
|
+
return func.call(bindings?.raw, event, event, $ctx);
|
|
11493
11533
|
};
|
|
11494
11534
|
}
|
|
11495
11535
|
/**
|
|
@@ -11500,8 +11540,8 @@
|
|
|
11500
11540
|
* @param identifiers The list of identifiers that are available in bindings.
|
|
11501
11541
|
* @returns The rewritten expression.
|
|
11502
11542
|
*/
|
|
11503
|
-
#rewriteExpression(expression, identifiers) {
|
|
11504
|
-
return ExpressionUtils.rewriteExpression(expression, identifiers);
|
|
11543
|
+
#rewriteExpression(expression, identifiers, options) {
|
|
11544
|
+
return ExpressionUtils.rewriteExpression(expression, identifiers, options);
|
|
11505
11545
|
}
|
|
11506
11546
|
}
|
|
11507
11547
|
|