assign-gingerly 0.0.69 โ 0.0.71
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/DX/emojis.js +4 -1
- package/DX/emojis.ts +4 -1
- package/DX/installForwarding.ts +3 -2
- package/DX/pinCorrector.ts +1 -1
- package/assignFrom.js +83 -9
- package/assignFrom.ts +82 -9
- package/assignFromAsync.ts +2 -1
- package/assignGingerly.js +4 -3
- package/assignGingerly.ts +6 -112
- package/eachTime.ts +1 -1
- package/handleIshProperty.ts +1 -1
- package/inferencer/types/assign-gingerly/types.d.ts +31 -0
- package/object-extension.ts +2 -1
- package/package.json +21 -17
- package/types/assign-gingerly/types.d.ts +31 -0
package/DX/emojis.js
CHANGED
|
@@ -34,11 +34,14 @@ export const akaMethods = {
|
|
|
34
34
|
'๐งบ': 'querySelectorAll',
|
|
35
35
|
'+': 'add',
|
|
36
36
|
'๐งฌ': 'cloneNode',
|
|
37
|
-
'๐ค': 'textContent',
|
|
38
37
|
};
|
|
39
38
|
export const aka = {
|
|
40
39
|
'ยฉ๏ธ': 'content?.cloneNode?.true',
|
|
41
40
|
//'๐': 'clone?.querySelector'
|
|
41
|
+
// textContent is a property, not a method โ aliasing it via akaMethods
|
|
42
|
+
// would add it to withMethods and turn `?.textContent` assignments into
|
|
43
|
+
// silently-skipped method calls.
|
|
44
|
+
'๐ค': 'textContent',
|
|
42
45
|
};
|
|
43
46
|
export const emojis = {
|
|
44
47
|
builtInEmoji,
|
package/DX/emojis.ts
CHANGED
|
@@ -36,12 +36,15 @@ export const akaMethods: Record<string, string> = {
|
|
|
36
36
|
'๐งบ': 'querySelectorAll',
|
|
37
37
|
'+': 'add',
|
|
38
38
|
'๐งฌ': 'cloneNode',
|
|
39
|
-
'๐ค': 'textContent',
|
|
40
39
|
};
|
|
41
40
|
|
|
42
41
|
export const aka: Record<string, string> = {
|
|
43
42
|
'ยฉ๏ธ': 'content?.cloneNode?.true',
|
|
44
43
|
//'๐': 'clone?.querySelector'
|
|
44
|
+
// textContent is a property, not a method โ aliasing it via akaMethods
|
|
45
|
+
// would add it to withMethods and turn `?.textContent` assignments into
|
|
46
|
+
// silently-skipped method calls.
|
|
47
|
+
'๐ค': 'textContent',
|
|
45
48
|
};
|
|
46
49
|
|
|
47
50
|
|
package/DX/installForwarding.ts
CHANGED
|
@@ -21,8 +21,9 @@
|
|
|
21
21
|
* // Now el.command delegates to el.behaviors.commandBehavior.command
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import { resolveValue
|
|
25
|
-
import
|
|
24
|
+
import { resolveValue} from '../resolveValues.js';
|
|
25
|
+
import {ResolveValuesOptions, IAssignGingerlyOptions} from '../types/assign-gingerly/types.js';
|
|
26
|
+
import assignGingerly, { } from '../assignGingerly.js';
|
|
26
27
|
|
|
27
28
|
export interface InstallForwardingOptions extends ResolveValuesOptions, IAssignGingerlyOptions {}
|
|
28
29
|
|
package/DX/pinCorrector.ts
CHANGED
|
@@ -14,7 +14,7 @@ function computeChildPath(root: Element, target: Element): number[] | null {
|
|
|
14
14
|
let current: Element | null = target;
|
|
15
15
|
|
|
16
16
|
while (current && current !== root) {
|
|
17
|
-
const parent = current.parentElement;
|
|
17
|
+
const parent = current.parentElement as HTMLElement | null;
|
|
18
18
|
if (!parent) return null;
|
|
19
19
|
const idx = Array.prototype.indexOf.call(parent.children, current);
|
|
20
20
|
if (idx === -1) return null;
|
package/assignFrom.js
CHANGED
|
@@ -68,6 +68,29 @@ function resolveTernaryValue(value, source, options) {
|
|
|
68
68
|
}
|
|
69
69
|
return value;
|
|
70
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Check if a value in a result position is a nested ternary expression.
|
|
73
|
+
* Trigger: an array of length โฅ 2 whose first element is a `?.`-prefixed
|
|
74
|
+
* string (truthiness mode) or an array (comparison mode). Other arrays
|
|
75
|
+
* are treated as literal values.
|
|
76
|
+
*/
|
|
77
|
+
function isNestedTernary(value) {
|
|
78
|
+
if (!Array.isArray(value) || value.length < 2)
|
|
79
|
+
return false;
|
|
80
|
+
const first = value[0];
|
|
81
|
+
return Array.isArray(first) || (typeof first === 'string' && first.startsWith('?.'));
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Resolve a value in a result position (then/else/neither, comparison results,
|
|
85
|
+
* chain candidates). Nested ternary expressions are evaluated recursively;
|
|
86
|
+
* everything else goes through resolveTernaryValue.
|
|
87
|
+
*/
|
|
88
|
+
function resolveResult(value, source, options) {
|
|
89
|
+
if (isNestedTernary(value)) {
|
|
90
|
+
return evaluateTernary(value, source, options);
|
|
91
|
+
}
|
|
92
|
+
return resolveTernaryValue(value, source, options);
|
|
93
|
+
}
|
|
71
94
|
/**
|
|
72
95
|
* Evaluate a ?= ternary expression.
|
|
73
96
|
*
|
|
@@ -77,8 +100,12 @@ function resolveTernaryValue(value, source, options) {
|
|
|
77
100
|
* - [ifTrue, trueResult, falseResult, neither] โ three-state (true/false/nullish)
|
|
78
101
|
* - [[lhs, rhs], ifEqual, ifNotEqual?] โ equality comparison
|
|
79
102
|
* - [[lhs, rhs], ifEqual] โ equality guard
|
|
103
|
+
* - [c1, '||', c2, '||', c3, ...] โ first truthy candidate (c1 || c2 || c3)
|
|
104
|
+
* - [c1, '??', c2, '??', c3, ...] โ first non-nullish candidate (c1 ?? c2 ?? c3)
|
|
105
|
+
* - [c1, t1, [c2, t2, e2]] โ nested ternary in any result position
|
|
80
106
|
*
|
|
81
|
-
* Returns
|
|
107
|
+
* Returns TERNARY_SKIP to signal "skip assignment" (guard forms when condition
|
|
108
|
+
* not met). A skip from a nested guard propagates outward.
|
|
82
109
|
*/
|
|
83
110
|
const TERNARY_SKIP = Symbol('ternary-skip');
|
|
84
111
|
function evaluateTernary(arr, source, options) {
|
|
@@ -90,10 +117,10 @@ function evaluateTernary(arr, source, options) {
|
|
|
90
117
|
// Equality: [[lhs, rhs], result, elseResult?]
|
|
91
118
|
const rhs = resolveTernaryValue(condition[1], source, options);
|
|
92
119
|
if (lhs === rhs) {
|
|
93
|
-
return
|
|
120
|
+
return resolveResult(arr[1], source, options);
|
|
94
121
|
}
|
|
95
122
|
else {
|
|
96
|
-
return arr.length > 2 ?
|
|
123
|
+
return arr.length > 2 ? resolveResult(arr[2], source, options) : TERNARY_SKIP;
|
|
97
124
|
}
|
|
98
125
|
}
|
|
99
126
|
else {
|
|
@@ -102,32 +129,79 @@ function evaluateTernary(arr, source, options) {
|
|
|
102
129
|
const rhs = resolveTernaryValue(condition[2], source, options);
|
|
103
130
|
const satisfied = compareWithOp(lhs, op, rhs);
|
|
104
131
|
if (satisfied) {
|
|
105
|
-
return
|
|
132
|
+
return resolveResult(arr[1], source, options);
|
|
106
133
|
}
|
|
107
134
|
else {
|
|
108
|
-
return arr.length > 2 ?
|
|
135
|
+
return arr.length > 2 ? resolveResult(arr[2], source, options) : TERNARY_SKIP;
|
|
109
136
|
}
|
|
110
137
|
}
|
|
111
138
|
}
|
|
112
139
|
else {
|
|
140
|
+
// Chain shortcut: [c1, '||', c2, ...] or [c1, '??', c2, ...]
|
|
141
|
+
// Checked before the length-based dispatch so a length-4 chain
|
|
142
|
+
// isn't misread as the three-state form.
|
|
143
|
+
if (arr[1] === '||' || arr[1] === '??') {
|
|
144
|
+
return evaluateChain(arr, source, options);
|
|
145
|
+
}
|
|
113
146
|
// Truthiness mode
|
|
114
147
|
const resolved = resolveTernaryValue(condition, source, options);
|
|
115
148
|
if (arr.length === 4) {
|
|
116
149
|
// [ifTrue, trueResult, falseResult, neitherResult]
|
|
117
150
|
if (resolved == null)
|
|
118
|
-
return
|
|
119
|
-
return resolved ?
|
|
151
|
+
return resolveResult(arr[3], source, options);
|
|
152
|
+
return resolved ? resolveResult(arr[1], source, options) : resolveResult(arr[2], source, options);
|
|
120
153
|
}
|
|
121
154
|
else if (arr.length === 3) {
|
|
122
155
|
// [ifTruthy, thenResult, elseResult]
|
|
123
|
-
return resolved ?
|
|
156
|
+
return resolved ? resolveResult(arr[1], source, options) : resolveResult(arr[2], source, options);
|
|
124
157
|
}
|
|
125
158
|
else {
|
|
126
159
|
// [ifTruthy, thenResult] โ guard, skip if falsy
|
|
127
|
-
return resolved ?
|
|
160
|
+
return resolved ? resolveResult(arr[1], source, options) : TERNARY_SKIP;
|
|
128
161
|
}
|
|
129
162
|
}
|
|
130
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Evaluate a chain shortcut: [c1, '||', c2, ...] or [c1, '??', c2, ...].
|
|
166
|
+
*
|
|
167
|
+
* - '||' returns the first truthy candidate (JS `c1 || c2 || c3`).
|
|
168
|
+
* - '??' returns the first non-nullish candidate (JS `c1 ?? c2 ?? c3`).
|
|
169
|
+
*
|
|
170
|
+
* Candidates sit at even indices and are resolved lazily โ evaluation stops
|
|
171
|
+
* at the first match. What happens when no candidate passes depends on the
|
|
172
|
+
* trailing element:
|
|
173
|
+
* - Ends with a candidate ([c1, '||', c2]): the last candidate doubles as the
|
|
174
|
+
* fallback (returned even when it fails the test, matching JS semantics).
|
|
175
|
+
* - Ends with a non-marker element after the last candidate ([c1, '||', c2, fb]):
|
|
176
|
+
* that element is the explicit fallback.
|
|
177
|
+
* - Ends with a dangling marker ([c1, '||'] or [c1, '||', c2, '||']): guard form โ
|
|
178
|
+
* nothing to assign (TERNARY_SKIP).
|
|
179
|
+
*
|
|
180
|
+
* Mixing marker types in one chain is not supported; the first marker sets the mode.
|
|
181
|
+
*
|
|
182
|
+
* Candidates may themselves be nested ternaries (see resolveResult). A nested
|
|
183
|
+
* guard that skips counts as a failed candidate โ the chain continues; if it
|
|
184
|
+
* was the final fallback, the skip propagates.
|
|
185
|
+
*/
|
|
186
|
+
function evaluateChain(arr, source, options) {
|
|
187
|
+
const isNullish = arr[1] === '??';
|
|
188
|
+
const n = arr.length;
|
|
189
|
+
const endsWithMarker = arr[n - 1] === '||' || arr[n - 1] === '??';
|
|
190
|
+
const lastCandidateIdx = n - (endsWithMarker || n % 2 === 0 ? 2 : 1);
|
|
191
|
+
let lastVal;
|
|
192
|
+
for (let i = 0; i <= lastCandidateIdx; i += 2) {
|
|
193
|
+
lastVal = resolveResult(arr[i], source, options);
|
|
194
|
+
if (lastVal === TERNARY_SKIP)
|
|
195
|
+
continue; // nested guard skipped โ try next candidate
|
|
196
|
+
const pass = isNullish ? lastVal != null : !!lastVal;
|
|
197
|
+
if (pass)
|
|
198
|
+
return lastVal;
|
|
199
|
+
}
|
|
200
|
+
if (endsWithMarker)
|
|
201
|
+
return TERNARY_SKIP;
|
|
202
|
+
// Odd count: last candidate doubles as fallback. Even count: explicit trailing fallback.
|
|
203
|
+
return n % 2 === 1 ? lastVal : resolveResult(arr[n - 1], source, options);
|
|
204
|
+
}
|
|
131
205
|
/**
|
|
132
206
|
* Compare two values with a given operator.
|
|
133
207
|
*/
|
package/assignFrom.ts
CHANGED
|
@@ -79,6 +79,30 @@ function resolveTernaryValue(value: any, source: any, options: AssignFromOptions
|
|
|
79
79
|
return value;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Check if a value in a result position is a nested ternary expression.
|
|
84
|
+
* Trigger: an array of length โฅ 2 whose first element is a `?.`-prefixed
|
|
85
|
+
* string (truthiness mode) or an array (comparison mode). Other arrays
|
|
86
|
+
* are treated as literal values.
|
|
87
|
+
*/
|
|
88
|
+
function isNestedTernary(value: any): boolean {
|
|
89
|
+
if (!Array.isArray(value) || value.length < 2) return false;
|
|
90
|
+
const first = value[0];
|
|
91
|
+
return Array.isArray(first) || (typeof first === 'string' && first.startsWith('?.'));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Resolve a value in a result position (then/else/neither, comparison results,
|
|
96
|
+
* chain candidates). Nested ternary expressions are evaluated recursively;
|
|
97
|
+
* everything else goes through resolveTernaryValue.
|
|
98
|
+
*/
|
|
99
|
+
function resolveResult(value: any, source: any, options: AssignFromOptions): any {
|
|
100
|
+
if (isNestedTernary(value)) {
|
|
101
|
+
return evaluateTernary(value, source, options);
|
|
102
|
+
}
|
|
103
|
+
return resolveTernaryValue(value, source, options);
|
|
104
|
+
}
|
|
105
|
+
|
|
82
106
|
/**
|
|
83
107
|
* Evaluate a ?= ternary expression.
|
|
84
108
|
*
|
|
@@ -88,8 +112,12 @@ function resolveTernaryValue(value: any, source: any, options: AssignFromOptions
|
|
|
88
112
|
* - [ifTrue, trueResult, falseResult, neither] โ three-state (true/false/nullish)
|
|
89
113
|
* - [[lhs, rhs], ifEqual, ifNotEqual?] โ equality comparison
|
|
90
114
|
* - [[lhs, rhs], ifEqual] โ equality guard
|
|
115
|
+
* - [c1, '||', c2, '||', c3, ...] โ first truthy candidate (c1 || c2 || c3)
|
|
116
|
+
* - [c1, '??', c2, '??', c3, ...] โ first non-nullish candidate (c1 ?? c2 ?? c3)
|
|
117
|
+
* - [c1, t1, [c2, t2, e2]] โ nested ternary in any result position
|
|
91
118
|
*
|
|
92
|
-
* Returns
|
|
119
|
+
* Returns TERNARY_SKIP to signal "skip assignment" (guard forms when condition
|
|
120
|
+
* not met). A skip from a nested guard propagates outward.
|
|
93
121
|
*/
|
|
94
122
|
const TERNARY_SKIP = Symbol('ternary-skip');
|
|
95
123
|
|
|
@@ -103,9 +131,9 @@ function evaluateTernary(arr: any[], source: any, options: AssignFromOptions): a
|
|
|
103
131
|
// Equality: [[lhs, rhs], result, elseResult?]
|
|
104
132
|
const rhs = resolveTernaryValue(condition[1], source, options);
|
|
105
133
|
if (lhs === rhs) {
|
|
106
|
-
return
|
|
134
|
+
return resolveResult(arr[1], source, options);
|
|
107
135
|
} else {
|
|
108
|
-
return arr.length > 2 ?
|
|
136
|
+
return arr.length > 2 ? resolveResult(arr[2], source, options) : TERNARY_SKIP;
|
|
109
137
|
}
|
|
110
138
|
} else {
|
|
111
139
|
// Operator: [[lhs, op, rhs], result, elseResult?]
|
|
@@ -113,28 +141,73 @@ function evaluateTernary(arr: any[], source: any, options: AssignFromOptions): a
|
|
|
113
141
|
const rhs = resolveTernaryValue(condition[2], source, options);
|
|
114
142
|
const satisfied = compareWithOp(lhs, op, rhs);
|
|
115
143
|
if (satisfied) {
|
|
116
|
-
return
|
|
144
|
+
return resolveResult(arr[1], source, options);
|
|
117
145
|
} else {
|
|
118
|
-
return arr.length > 2 ?
|
|
146
|
+
return arr.length > 2 ? resolveResult(arr[2], source, options) : TERNARY_SKIP;
|
|
119
147
|
}
|
|
120
148
|
}
|
|
121
149
|
} else {
|
|
150
|
+
// Chain shortcut: [c1, '||', c2, ...] or [c1, '??', c2, ...]
|
|
151
|
+
// Checked before the length-based dispatch so a length-4 chain
|
|
152
|
+
// isn't misread as the three-state form.
|
|
153
|
+
if (arr[1] === '||' || arr[1] === '??') {
|
|
154
|
+
return evaluateChain(arr, source, options);
|
|
155
|
+
}
|
|
122
156
|
// Truthiness mode
|
|
123
157
|
const resolved = resolveTernaryValue(condition, source, options);
|
|
124
158
|
if (arr.length === 4) {
|
|
125
159
|
// [ifTrue, trueResult, falseResult, neitherResult]
|
|
126
|
-
if (resolved == null) return
|
|
127
|
-
return resolved ?
|
|
160
|
+
if (resolved == null) return resolveResult(arr[3], source, options);
|
|
161
|
+
return resolved ? resolveResult(arr[1], source, options) : resolveResult(arr[2], source, options);
|
|
128
162
|
} else if (arr.length === 3) {
|
|
129
163
|
// [ifTruthy, thenResult, elseResult]
|
|
130
|
-
return resolved ?
|
|
164
|
+
return resolved ? resolveResult(arr[1], source, options) : resolveResult(arr[2], source, options);
|
|
131
165
|
} else {
|
|
132
166
|
// [ifTruthy, thenResult] โ guard, skip if falsy
|
|
133
|
-
return resolved ?
|
|
167
|
+
return resolved ? resolveResult(arr[1], source, options) : TERNARY_SKIP;
|
|
134
168
|
}
|
|
135
169
|
}
|
|
136
170
|
}
|
|
137
171
|
|
|
172
|
+
/**
|
|
173
|
+
* Evaluate a chain shortcut: [c1, '||', c2, ...] or [c1, '??', c2, ...].
|
|
174
|
+
*
|
|
175
|
+
* - '||' returns the first truthy candidate (JS `c1 || c2 || c3`).
|
|
176
|
+
* - '??' returns the first non-nullish candidate (JS `c1 ?? c2 ?? c3`).
|
|
177
|
+
*
|
|
178
|
+
* Candidates sit at even indices and are resolved lazily โ evaluation stops
|
|
179
|
+
* at the first match. What happens when no candidate passes depends on the
|
|
180
|
+
* trailing element:
|
|
181
|
+
* - Ends with a candidate ([c1, '||', c2]): the last candidate doubles as the
|
|
182
|
+
* fallback (returned even when it fails the test, matching JS semantics).
|
|
183
|
+
* - Ends with a non-marker element after the last candidate ([c1, '||', c2, fb]):
|
|
184
|
+
* that element is the explicit fallback.
|
|
185
|
+
* - Ends with a dangling marker ([c1, '||'] or [c1, '||', c2, '||']): guard form โ
|
|
186
|
+
* nothing to assign (TERNARY_SKIP).
|
|
187
|
+
*
|
|
188
|
+
* Mixing marker types in one chain is not supported; the first marker sets the mode.
|
|
189
|
+
*
|
|
190
|
+
* Candidates may themselves be nested ternaries (see resolveResult). A nested
|
|
191
|
+
* guard that skips counts as a failed candidate โ the chain continues; if it
|
|
192
|
+
* was the final fallback, the skip propagates.
|
|
193
|
+
*/
|
|
194
|
+
function evaluateChain(arr: any[], source: any, options: AssignFromOptions): any {
|
|
195
|
+
const isNullish = arr[1] === '??';
|
|
196
|
+
const n = arr.length;
|
|
197
|
+
const endsWithMarker = arr[n - 1] === '||' || arr[n - 1] === '??';
|
|
198
|
+
const lastCandidateIdx = n - (endsWithMarker || n % 2 === 0 ? 2 : 1);
|
|
199
|
+
let lastVal: any;
|
|
200
|
+
for (let i = 0; i <= lastCandidateIdx; i += 2) {
|
|
201
|
+
lastVal = resolveResult(arr[i], source, options);
|
|
202
|
+
if (lastVal === TERNARY_SKIP) continue; // nested guard skipped โ try next candidate
|
|
203
|
+
const pass = isNullish ? lastVal != null : !!lastVal;
|
|
204
|
+
if (pass) return lastVal;
|
|
205
|
+
}
|
|
206
|
+
if (endsWithMarker) return TERNARY_SKIP;
|
|
207
|
+
// Odd count: last candidate doubles as fallback. Even count: explicit trailing fallback.
|
|
208
|
+
return n % 2 === 1 ? lastVal : resolveResult(arr[n - 1], source, options);
|
|
209
|
+
}
|
|
210
|
+
|
|
138
211
|
/**
|
|
139
212
|
* Compare two values with a given operator.
|
|
140
213
|
*/
|
package/assignFromAsync.ts
CHANGED
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
* // target is now { color: 'red', text: 'Hello' }
|
|
21
21
|
*/
|
|
22
22
|
import { resolveValues } from './resolveValues.js';
|
|
23
|
-
import
|
|
23
|
+
import {IAssignGingerlyOptions} from './types/assign-gingerly/types.js';
|
|
24
|
+
import assignGingerly from './assignGingerly.js';
|
|
24
25
|
import type { AssignPermissions } from './isAllowedImportPath.js';
|
|
25
26
|
import type { AssignFromHandler, AssignFromHandlerConstructor } from './types/assign-gingerly/types.js';
|
|
26
27
|
import {
|
package/assignGingerly.js
CHANGED
|
@@ -571,9 +571,9 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
571
571
|
: undefined;
|
|
572
572
|
const registry = options?.registry instanceof EnhancementRegistry
|
|
573
573
|
? options.registry
|
|
574
|
-
: options?.registry
|
|
575
|
-
|
|
576
|
-
|
|
574
|
+
// : options?.registry
|
|
575
|
+
// ? new options.registry()
|
|
576
|
+
: undefined;
|
|
577
577
|
// Convert Symbol.for string keys to actual symbols and apply aliases
|
|
578
578
|
const processedSource = {};
|
|
579
579
|
for (const key of Object.keys(source)) {
|
|
@@ -653,6 +653,7 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
653
653
|
lhsParent = ensureNestedPath(target, pathParts);
|
|
654
654
|
lhsValue = lhsParent[lhsKey];
|
|
655
655
|
}
|
|
656
|
+
//TODO: this logic seems to occur twice at least. Maybe make it a method?
|
|
656
657
|
// Event handler: Element LHS + object RHS with 'on' property
|
|
657
658
|
if (lhsValue instanceof Element && value && typeof value === 'object' && !Array.isArray(value) && 'on' in value) {
|
|
658
659
|
const capturedLhs = lhsValue;
|
package/assignGingerly.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
3
|
import { EnhancementConfig } from "./types/assign-gingerly/types";
|
|
4
|
-
import type { FeatureConfigsMap } from "./types/assign-gingerly/types";
|
|
4
|
+
import type { AssignFromOptions, FeatureConfigsMap, IAssignGingerlyOptions } from "./types/assign-gingerly/types";
|
|
5
5
|
import type { AssignPermissions } from "./isAllowedImportPath.js";
|
|
6
6
|
import { normalizeAliasOptions } from './getValues.js';
|
|
7
7
|
|
|
@@ -32,113 +32,6 @@ export interface ItemscopeManagerConfig<T = any> {
|
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
/**
|
|
36
|
-
* Interface for the options passed to assignGingerly
|
|
37
|
-
*/
|
|
38
|
-
export interface IAssignGingerlyOptions {
|
|
39
|
-
registry?: typeof EnhancementRegistry | EnhancementRegistry;
|
|
40
|
-
bypassChecks?: boolean;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* List of property names that should be treated as methods to call
|
|
44
|
-
* rather than properties to assign.
|
|
45
|
-
*
|
|
46
|
-
* When a path segment matches a name in this array/set:
|
|
47
|
-
* - If the property is a function, call it with appropriate arguments
|
|
48
|
-
* - For the last segment: use RHS value as argument (spread if array)
|
|
49
|
-
* - For middle segments: use next segment as string argument (if next is not also a method)
|
|
50
|
-
* - If consecutive segments are both methods, first is called with no arguments
|
|
51
|
-
* - If the property is not a function, silently skip
|
|
52
|
-
*
|
|
53
|
-
* Example:
|
|
54
|
-
* assignGingerly(element, {
|
|
55
|
-
* '?.classList?.add': 'myClass'
|
|
56
|
-
* }, { withMethods: ['add'] });
|
|
57
|
-
* // Calls: element.classList.add('myClass')
|
|
58
|
-
*
|
|
59
|
-
* Chained methods:
|
|
60
|
-
* assignGingerly(elementRef, {
|
|
61
|
-
* '?.deref?.querySelector?.myElement?.classList?.add': 'active'
|
|
62
|
-
* }, { withMethods: ['deref', 'querySelector', 'add'] });
|
|
63
|
-
* // Calls: elementRef.deref().querySelector('myElement').classList.add('active')
|
|
64
|
-
*/
|
|
65
|
-
withMethods?: string[] | Set<string>;
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Alias mappings for property and method names.
|
|
69
|
-
* Allows shorter, customizable shortcuts in path expressions.
|
|
70
|
-
*
|
|
71
|
-
* Aliases are substituted before path evaluation, matching complete tokens
|
|
72
|
-
* between `?.` delimiters (not substrings).
|
|
73
|
-
*
|
|
74
|
-
* Reserved characters (cannot be used in aliases): space, backtick (`)
|
|
75
|
-
*
|
|
76
|
-
* Example:
|
|
77
|
-
* assignGingerly(element, {
|
|
78
|
-
* '?.$?.my-element?.c?.+': 'highlighted'
|
|
79
|
-
* }, {
|
|
80
|
-
* withMethods: ['querySelector', 'add'],
|
|
81
|
-
* aka: { '$': 'querySelector', 'c': 'classList', '+': 'add' }
|
|
82
|
-
* });
|
|
83
|
-
* // Equivalent to: element.querySelector('my-element').classList.add('highlighted')
|
|
84
|
-
*/
|
|
85
|
-
aka?: Record<string, string>;
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Shorthand for binding method aliases from the source object.
|
|
89
|
-
* Each entry maps an alias to a method name, and is normalized into
|
|
90
|
-
* the existing withMethods + aka behavior.
|
|
91
|
-
*/
|
|
92
|
-
akaMethods?: Record<string, string>;
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* AbortSignal for cleaning up reactive subscriptions (@eachTime)
|
|
96
|
-
* Required when using @eachTime symbol for reactive iteration
|
|
97
|
-
* When the signal is aborted, all event listeners are automatically removed
|
|
98
|
-
*
|
|
99
|
-
* Example:
|
|
100
|
-
* const controller = new AbortController();
|
|
101
|
-
* assignGingerly(div, {
|
|
102
|
-
* '?.mountObserver?.@eachTime?.classList?.add': 'highlighted'
|
|
103
|
-
* }, {
|
|
104
|
-
* withMethods: ['add'],
|
|
105
|
-
* signal: controller.signal
|
|
106
|
-
* });
|
|
107
|
-
* // Later: controller.abort(); // Cleanup all listeners
|
|
108
|
-
*/
|
|
109
|
-
signal?: AbortSignal;
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* List of property names that should be treated as async methods.
|
|
113
|
-
* Works together with withMethods โ async methods are awaited before
|
|
114
|
-
* continuing the chain.
|
|
115
|
-
*
|
|
116
|
-
* The path evaluation for keys containing async methods is fire-and-forget:
|
|
117
|
-
* assignGingerly remains synchronous and returns immediately. The async
|
|
118
|
-
* chain completes in the background.
|
|
119
|
-
*
|
|
120
|
-
* NOTE: Interaction with @each and @eachTime is not yet implemented.
|
|
121
|
-
*
|
|
122
|
-
* Example:
|
|
123
|
-
* assignGingerly(el, {
|
|
124
|
-
* '?.whenFeatureReady?.photoTaker?.someProp': 'hello'
|
|
125
|
-
* }, { withAsyncMethods: ['whenFeatureReady'] });
|
|
126
|
-
* // Calls: (await el.whenFeatureReady('photoTaker')).someProp = 'hello'
|
|
127
|
-
*/
|
|
128
|
-
withAsyncMethods?: string[] | Set<string>;
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Bulk enhancement application via EMC JSON configs.
|
|
132
|
-
* Finds matching elements and spawns enhancements on them.
|
|
133
|
-
* Fire-and-forget (async) โ assignGingerly remains synchronous.
|
|
134
|
-
*
|
|
135
|
-
* @example
|
|
136
|
-
* enhance: [
|
|
137
|
-
* { emc: 'be-bound/emc.json', matching: '[name]' },
|
|
138
|
-
* ]
|
|
139
|
-
*/
|
|
140
|
-
enhance?: Array<{ emc: string; matching?: string; parse?: boolean }>;
|
|
141
|
-
}
|
|
142
35
|
|
|
143
36
|
/**
|
|
144
37
|
* GUID for global instance map storage to ensure uniqueness across package versions
|
|
@@ -779,8 +672,8 @@ export function assignGingerly(
|
|
|
779
672
|
|
|
780
673
|
const registry = options?.registry instanceof EnhancementRegistry
|
|
781
674
|
? options.registry
|
|
782
|
-
: options?.registry
|
|
783
|
-
? new options.registry()
|
|
675
|
+
// : options?.registry
|
|
676
|
+
// ? new options.registry()
|
|
784
677
|
: undefined;
|
|
785
678
|
|
|
786
679
|
// Convert Symbol.for string keys to actual symbols and apply aliases
|
|
@@ -867,12 +760,13 @@ export function assignGingerly(
|
|
|
867
760
|
lhsValue = lhsParent[lhsKey];
|
|
868
761
|
}
|
|
869
762
|
|
|
763
|
+
//TODO: this logic seems to occur twice at least. Maybe make it a method?
|
|
870
764
|
// Event handler: Element LHS + object RHS with 'on' property
|
|
871
765
|
if (lhsValue instanceof Element && value && typeof value === 'object' && !Array.isArray(value) && 'on' in value) {
|
|
872
766
|
const capturedLhs = lhsValue;
|
|
873
767
|
const capturedValue = value;
|
|
874
768
|
const capturedTarget = target;
|
|
875
|
-
const capturedOptions = options;
|
|
769
|
+
const capturedOptions = options as AssignFromOptions | undefined;
|
|
876
770
|
import('./handlers/addEventListener.js').then(({ attachEventListener }) => {
|
|
877
771
|
attachEventListener(capturedLhs, capturedValue, capturedTarget, capturedOptions?.from ?? capturedTarget, capturedOptions ?? {});
|
|
878
772
|
});
|
|
@@ -898,7 +792,7 @@ export function assignGingerly(
|
|
|
898
792
|
const capturedLhs = target[path];
|
|
899
793
|
const capturedValue = value;
|
|
900
794
|
const capturedTarget = target;
|
|
901
|
-
const capturedOptions = options;
|
|
795
|
+
const capturedOptions = options as AssignFromOptions | undefined;
|
|
902
796
|
import('./handlers/addEventListener.js').then(({ attachEventListener }) => {
|
|
903
797
|
attachEventListener(capturedLhs, capturedValue, capturedTarget, capturedOptions?.from ?? capturedTarget, capturedOptions ?? {});
|
|
904
798
|
});
|
package/eachTime.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Provides event-driven iteration over elements as they mount
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type { IAssignGingerlyOptions } from './
|
|
7
|
+
import type { IAssignGingerlyOptions } from './types/assign-gingerly/types.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Check if a value is an EventTarget
|
package/handleIshProperty.ts
CHANGED
|
@@ -272,6 +272,37 @@ export interface IAssignGingerlyOptions {
|
|
|
272
272
|
* When the signal is aborted, all event listeners are automatically removed
|
|
273
273
|
*/
|
|
274
274
|
signal?: AbortSignal;
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* List of property names that should be treated as async methods.
|
|
278
|
+
* Works together with withMethods โ async methods are awaited before
|
|
279
|
+
* continuing the chain.
|
|
280
|
+
*
|
|
281
|
+
* The path evaluation for keys containing async methods is fire-and-forget:
|
|
282
|
+
* assignGingerly remains synchronous and returns immediately. The async
|
|
283
|
+
* chain completes in the background.
|
|
284
|
+
*
|
|
285
|
+
* NOTE: Interaction with @each and @eachTime is not yet implemented.
|
|
286
|
+
*
|
|
287
|
+
* Example:
|
|
288
|
+
* assignGingerly(el, {
|
|
289
|
+
* '?.whenFeatureReady?.photoTaker?.someProp': 'hello'
|
|
290
|
+
* }, { withAsyncMethods: ['whenFeatureReady'] });
|
|
291
|
+
* // Calls: (await el.whenFeatureReady('photoTaker')).someProp = 'hello'
|
|
292
|
+
*/
|
|
293
|
+
withAsyncMethods?: string[] | Set<string>;
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Bulk enhancement application via EMC JSON configs.
|
|
297
|
+
* Finds matching elements and spawns enhancements on them.
|
|
298
|
+
* Fire-and-forget (async) โ assignGingerly remains synchronous.
|
|
299
|
+
*
|
|
300
|
+
* @example
|
|
301
|
+
* enhance: [
|
|
302
|
+
* { emc: 'be-bound/emc.json', matching: '[name]' },
|
|
303
|
+
* ]
|
|
304
|
+
*/
|
|
305
|
+
enhance?: Array<{ emc: string; matching?: string; parse?: boolean }>;
|
|
275
306
|
}
|
|
276
307
|
|
|
277
308
|
/**
|
package/object-extension.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import assignGingerly, { EnhancementRegistry, ItemscopeRegistry,
|
|
1
|
+
import assignGingerly, { EnhancementRegistry, ItemscopeRegistry, getInstanceMap } from './assignGingerly.js';
|
|
2
|
+
import {IAssignGingerlyOptions,} from './types/assign-gingerly/types.js';
|
|
2
3
|
import assignTentatively from './assignTentatively.js';
|
|
3
4
|
import type { IAssignTentativelyOptions } from './types/assign-gingerly/types.js';
|
|
4
5
|
import { EnhancementConfig, SpawnContext } from './types/assign-gingerly/types.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "assign-gingerly",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.71",
|
|
4
4
|
"description": "This package provides a utility function for carefully merging one object into another.",
|
|
5
5
|
"homepage": "https://github.com/bahrus/assign-gingerly#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -111,6 +111,26 @@
|
|
|
111
111
|
"default": "./handlers/manageTemplateList.js",
|
|
112
112
|
"types": "./handlers/manageTemplateList.ts"
|
|
113
113
|
},
|
|
114
|
+
"./handlers/nudge.js": {
|
|
115
|
+
"default": "./handlers/nudge.js",
|
|
116
|
+
"types": "./handlers/nudge.ts"
|
|
117
|
+
},
|
|
118
|
+
"./handlers/arr.js": {
|
|
119
|
+
"default": "./handlers/arr.js",
|
|
120
|
+
"types": "./handlers/arr.ts"
|
|
121
|
+
},
|
|
122
|
+
"./inferencer/inferencer.js": {
|
|
123
|
+
"default": "./inferencer/inferencer.js",
|
|
124
|
+
"types": "./inferencer/inferencer.ts"
|
|
125
|
+
},
|
|
126
|
+
"./inferencer/upSearch.js": {
|
|
127
|
+
"default": "./inferencer/upSearch.js",
|
|
128
|
+
"types": "./inferencer/upSearch.ts"
|
|
129
|
+
},
|
|
130
|
+
"./inferencer/withScopePerimeter.js": {
|
|
131
|
+
"default": "./inferencer/withScopePerimeter.js",
|
|
132
|
+
"types": "./inferencer/withScopePerimeter.ts"
|
|
133
|
+
},
|
|
114
134
|
"./DX/paths.js": {
|
|
115
135
|
"default": "./DX/paths.js",
|
|
116
136
|
"types": "./DX/paths.ts"
|
|
@@ -143,14 +163,6 @@
|
|
|
143
163
|
"default": "./beVigilant.js",
|
|
144
164
|
"types": "./beVigilant.ts"
|
|
145
165
|
},
|
|
146
|
-
"./inferencer/inferencer.js": {
|
|
147
|
-
"default": "./inferencer/inferencer.js",
|
|
148
|
-
"types": "./inferencer/inferencer.ts"
|
|
149
|
-
},
|
|
150
|
-
"./inferencer/withScopePerimeter.js": {
|
|
151
|
-
"default": "./inferencer/withScopePerimeter.js",
|
|
152
|
-
"types": "./inferencer/withScopePerimeter.ts"
|
|
153
|
-
},
|
|
154
166
|
"./assignFromAsync.js": {
|
|
155
167
|
"default": "./assignFromAsync.js",
|
|
156
168
|
"types": "./assignFromAsync.ts"
|
|
@@ -178,14 +190,6 @@
|
|
|
178
190
|
"./evaluatePathWithAsyncMethods.js": {
|
|
179
191
|
"default": "./evaluatePathWithAsyncMethods.js",
|
|
180
192
|
"types": "./evaluatePathWithAsyncMethods.ts"
|
|
181
|
-
},
|
|
182
|
-
"./handlers/nudge.js": {
|
|
183
|
-
"default": "./handlers/nudge.js",
|
|
184
|
-
"types": "./handlers/nudge.ts"
|
|
185
|
-
},
|
|
186
|
-
"./handlers/arr.js": {
|
|
187
|
-
"default": "./handlers/arr.js",
|
|
188
|
-
"types": "./handlers/arr.ts"
|
|
189
193
|
}
|
|
190
194
|
},
|
|
191
195
|
"main": "index.js",
|
|
@@ -272,6 +272,37 @@ export interface IAssignGingerlyOptions {
|
|
|
272
272
|
* When the signal is aborted, all event listeners are automatically removed
|
|
273
273
|
*/
|
|
274
274
|
signal?: AbortSignal;
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* List of property names that should be treated as async methods.
|
|
278
|
+
* Works together with withMethods โ async methods are awaited before
|
|
279
|
+
* continuing the chain.
|
|
280
|
+
*
|
|
281
|
+
* The path evaluation for keys containing async methods is fire-and-forget:
|
|
282
|
+
* assignGingerly remains synchronous and returns immediately. The async
|
|
283
|
+
* chain completes in the background.
|
|
284
|
+
*
|
|
285
|
+
* NOTE: Interaction with @each and @eachTime is not yet implemented.
|
|
286
|
+
*
|
|
287
|
+
* Example:
|
|
288
|
+
* assignGingerly(el, {
|
|
289
|
+
* '?.whenFeatureReady?.photoTaker?.someProp': 'hello'
|
|
290
|
+
* }, { withAsyncMethods: ['whenFeatureReady'] });
|
|
291
|
+
* // Calls: (await el.whenFeatureReady('photoTaker')).someProp = 'hello'
|
|
292
|
+
*/
|
|
293
|
+
withAsyncMethods?: string[] | Set<string>;
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Bulk enhancement application via EMC JSON configs.
|
|
297
|
+
* Finds matching elements and spawns enhancements on them.
|
|
298
|
+
* Fire-and-forget (async) โ assignGingerly remains synchronous.
|
|
299
|
+
*
|
|
300
|
+
* @example
|
|
301
|
+
* enhance: [
|
|
302
|
+
* { emc: 'be-bound/emc.json', matching: '[name]' },
|
|
303
|
+
* ]
|
|
304
|
+
*/
|
|
305
|
+
enhance?: Array<{ emc: string; matching?: string; parse?: boolean }>;
|
|
275
306
|
}
|
|
276
307
|
|
|
277
308
|
/**
|