assign-gingerly 0.0.64 → 0.0.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/README.md +85 -26
- package/assignFrom.js +21 -19
- package/assignFrom.ts +28 -25
- package/assignFromAsync.js +10 -7
- package/assignFromAsync.ts +15 -98
- package/assignGingerly.js +54 -26
- package/assignGingerly.ts +62 -27
- package/assignTentatively.js +8 -0
- package/assignTentatively.ts +6 -0
- package/{builtInEmoji.ts → emojis.js} +46 -34
- package/emojis.ts +52 -0
- package/getValues.js +80 -25
- package/getValues.ts +85 -26
- package/handlers/addEventListener.js +153 -0
- package/handlers/addEventListener.ts +178 -0
- package/handlers/arr.js +13 -0
- package/handlers/arr.ts +13 -0
- package/handlers/lazyLoad.js +3 -3
- package/handlers/lazyLoad.ts +3 -3
- package/handlers/manageTemplateList.js +48 -21
- package/handlers/manageTemplateList.ts +51 -21
- package/handlers/nudge.js +23 -0
- package/handlers/nudge.ts +23 -0
- package/index.js +2 -0
- package/index.ts +2 -0
- package/inferencer/types/assign-gingerly/types.d.ts +327 -1
- package/inferencer/types/mount-observer/types.d.ts +10 -2
- package/inferencer/types/roundabout/types.d.ts +1 -1
- package/inferencer/types/three-peat/types.d.ts +18 -0
- package/object-extension.js +1 -1
- package/package.json +12 -4
- package/paths.ts +1 -1
- package/{withIdsCorrector.js → pinCorrector.js} +5 -5
- package/{withIdsCorrector.ts → pinCorrector.ts} +5 -5
- package/processHandlerCommands.js +4 -2
- package/processHandlerCommands.ts +4 -2
- package/resolveIdRef.js +8 -8
- package/resolveIdRef.ts +9 -9
- package/resolveValues.js +20 -88
- package/resolveValues.ts +17 -91
- package/types/assign-gingerly/types.d.ts +159 -3
- package/builtInEmoji.js +0 -26
package/assignFromAsync.ts
CHANGED
|
@@ -22,117 +22,31 @@
|
|
|
22
22
|
import { resolveValues } from './resolveValues.js';
|
|
23
23
|
import assignGingerly, { IAssignGingerlyOptions } from './assignGingerly.js';
|
|
24
24
|
import type { AssignPermissions } from './isAllowedImportPath.js';
|
|
25
|
+
import type { AssignFromHandler, AssignFromHandlerConstructor } from './types/assign-gingerly/types.js';
|
|
25
26
|
import {
|
|
26
27
|
expandSubstitutions, categorizeKeys, handleSpreads, isHandlerCommand
|
|
27
28
|
} from './assignFrom.js';
|
|
28
29
|
|
|
29
30
|
export interface AssignFromOptions extends IAssignGingerlyOptions {
|
|
30
|
-
/** Source object to resolve RHS path strings against */
|
|
31
31
|
from: any;
|
|
32
|
-
|
|
33
|
-
/** Protocol handlers (sync or async) */
|
|
34
32
|
protocols?: Record<string, (key: string) => any | Promise<any>>;
|
|
35
|
-
|
|
36
|
-
/** Loop variable bindings — expand pattern entries containing ${x} */
|
|
37
33
|
where_x_in?: string[];
|
|
38
|
-
/** Loop variable bindings — expand pattern entries containing ${y} */
|
|
39
34
|
where_y_in?: string[];
|
|
40
|
-
/** Loop variable bindings — expand pattern entries containing ${z} */
|
|
41
35
|
where_z_in?: string[];
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Cached element references by variable name.
|
|
45
|
-
* Used with `#[varName]` syntax in LHS keys for fast repeated element access.
|
|
46
|
-
*
|
|
47
|
-
* - String value: existing element ID (uses getElementById)
|
|
48
|
-
* - Object value: { qry: 'selector' } — finds element via querySelector on target, auto-assigns an ID
|
|
49
|
-
*
|
|
50
|
-
* Elements are cached via WeakRef with getElementById fallback on cache miss.
|
|
51
|
-
*/
|
|
52
|
-
withIds?: Record<string, string | { qry: string }>;
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Positional element references for use with `#[varName]` syntax.
|
|
56
|
-
* Resolves elements by child index path — no IDs assigned, no caching.
|
|
57
|
-
*
|
|
58
|
-
* - Array value: child index path (e.g., [0, 1] = target.children[0].children[1])
|
|
59
|
-
* - Object value: { path: [...], expect?: 'selector', fallback?: true }
|
|
60
|
-
* expect: validates via element.matches(), logs correction if wrong
|
|
61
|
-
* fallback: on mismatch, recovers via querySelector(expect)
|
|
62
|
-
*/
|
|
36
|
+
pin?: Record<string, string | { qry: string } | { path: number[]; expect?: string; fallback?: boolean }>;
|
|
63
37
|
at?: Record<string, number[] | { path: number[]; expect?: string; fallback?: boolean }>;
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Handler implementations scoped to this call.
|
|
67
|
-
* Key: the `do` name referenced in handler configs.
|
|
68
|
-
* Value: a class constructor, or an import path to dynamically load one.
|
|
69
|
-
*
|
|
70
|
-
* Import paths must be local (relative, absolute, or bare specifier — no cross-domain URLs).
|
|
71
|
-
* The module's default export is checked first; otherwise the first exported class
|
|
72
|
-
* with an `assign` method on its prototype is used.
|
|
73
|
-
*
|
|
74
|
-
* Built-in handlers (builtIns.*) auto-load without needing to be listed here.
|
|
75
|
-
*
|
|
76
|
-
* @example
|
|
77
|
-
* handlers: {
|
|
78
|
-
* 'my-list': MyListHandler, // class constructor
|
|
79
|
-
* 'my-chart': './handlers/chart.js', // dynamic import path
|
|
80
|
-
* 'vendor-widget': 'some-package/handler.js', // bare specifier (import map)
|
|
81
|
-
* }
|
|
82
|
-
*/
|
|
83
38
|
handlers?: Record<string, AssignFromHandlerConstructor | string>;
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Inferred assignments — automatically distribute source values to matching
|
|
87
|
-
* DOM elements based on structural conventions (itemprop, name, etc.).
|
|
88
|
-
*
|
|
89
|
-
* Uses the inferencer submodule to determine the correct property for each
|
|
90
|
-
* matched element (textContent, value, checked, dateTime, ish, etc.).
|
|
91
|
-
*
|
|
92
|
-
* @example
|
|
93
|
-
* infer: {
|
|
94
|
-
* byItemprop: ['user', 'name', 'email'], // or true for all source keys
|
|
95
|
-
* beVigilant: true, // watch for new matching elements (requires signal)
|
|
96
|
-
* }
|
|
97
|
-
*/
|
|
98
39
|
infer?: {
|
|
99
40
|
byItemprop?: string[] | true;
|
|
100
41
|
'|'?: string[] | true;
|
|
101
42
|
byName?: string[] | true | { props: string[] | true; outside: string };
|
|
102
43
|
'@'?: string[] | true | { props: string[] | true; outside: string };
|
|
103
|
-
/** Watch for new matching elements via MutationObserver. Requires options.signal for cleanup. */
|
|
104
44
|
beVigilant?: boolean;
|
|
105
45
|
};
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Bulk enhancement application via EMC JSON configs.
|
|
109
|
-
* Finds matching elements and spawns enhancements on them.
|
|
110
|
-
*
|
|
111
|
-
* Each entry specifies an EMC JSON path and optionally overrides the matching selector.
|
|
112
|
-
* Enhancements are auto-registered if not already present in the enhancement registry.
|
|
113
|
-
*
|
|
114
|
-
* No scope perimeter is applied — use mount-observer for reactive/scoped enhancement.
|
|
115
|
-
*
|
|
116
|
-
* @example
|
|
117
|
-
* enhance: [
|
|
118
|
-
* { emc: 'be-bound/emc.json', matching: '[name]' },
|
|
119
|
-
* { emc: 'be-observant/emc.json', matching: '[itemprop]' },
|
|
120
|
-
* ]
|
|
121
|
-
*/
|
|
122
46
|
enhance?: Array<{ emc: string; matching?: string; parse?: boolean }>;
|
|
123
47
|
}
|
|
124
48
|
|
|
125
|
-
|
|
126
|
-
* Interface for assignFrom handler classes.
|
|
127
|
-
* Handlers are invoked when a LHS key ends with ' =>'.
|
|
128
|
-
*/
|
|
129
|
-
export interface AssignFromHandler {
|
|
130
|
-
assign(lhsTarget: any, resolvedParams: Record<string, any>, options: AssignFromOptions): Promise<void> | void;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export interface AssignFromHandlerConstructor {
|
|
134
|
-
new (config: any): AssignFromHandler;
|
|
135
|
-
}
|
|
49
|
+
export type { AssignFromHandler, AssignFromHandlerConstructor };
|
|
136
50
|
|
|
137
51
|
// Module cache for processHandlerCommands — avoids await on dynamic import after first call
|
|
138
52
|
let _processHandlerCommands: any;
|
|
@@ -154,7 +68,9 @@ export async function assignFromAsync(
|
|
|
154
68
|
const resolved = await resolveValues(normalPattern, options.from, {
|
|
155
69
|
withMethods: options.withMethods,
|
|
156
70
|
aka: options.aka,
|
|
157
|
-
|
|
71
|
+
akaMethods: options.akaMethods,
|
|
72
|
+
protocols: options.protocols,
|
|
73
|
+
root: target
|
|
158
74
|
});
|
|
159
75
|
|
|
160
76
|
// Recursively handle "..." spread keys at all nesting levels
|
|
@@ -164,9 +80,10 @@ export async function assignFromAsync(
|
|
|
164
80
|
}
|
|
165
81
|
|
|
166
82
|
// Process #[x] normal keys — resolve element, then apply remaining path + value
|
|
167
|
-
if (idRefNormalKeys.length > 0 && (options.
|
|
168
|
-
const ids = { ...options.
|
|
83
|
+
if (idRefNormalKeys.length > 0 && (options.pin || options.at)) {
|
|
84
|
+
const ids = { ...options.pin, ...options.at };
|
|
169
85
|
const { resolveIdVariable, parseIdRef } = await import('./resolveIdRef.js');
|
|
86
|
+
const { withMethods, aka, akaMethods, protocols, from } = options;
|
|
170
87
|
for (const key of idRefNormalKeys) {
|
|
171
88
|
const parsed = parseIdRef(key);
|
|
172
89
|
if (!parsed) continue;
|
|
@@ -178,8 +95,8 @@ export async function assignFromAsync(
|
|
|
178
95
|
if (parsed.remainingPath) {
|
|
179
96
|
// Resolve the RHS value
|
|
180
97
|
const resolvedValue = await resolveValues(
|
|
181
|
-
{ __v: value },
|
|
182
|
-
{ withMethods
|
|
98
|
+
{ __v: value }, from,
|
|
99
|
+
{ withMethods, aka, akaMethods, protocols, root: el }
|
|
183
100
|
);
|
|
184
101
|
// Apply remaining path on the resolved element
|
|
185
102
|
assignGingerly(el, { [parsed.remainingPath]: resolvedValue.__v }, options);
|
|
@@ -187,8 +104,8 @@ export async function assignFromAsync(
|
|
|
187
104
|
// No remaining path — resolve and assign directly to the element
|
|
188
105
|
const resolvedValue = await resolveValues(
|
|
189
106
|
typeof value === 'object' && value !== null ? value : { __v: value },
|
|
190
|
-
|
|
191
|
-
{ withMethods
|
|
107
|
+
from,
|
|
108
|
+
{ withMethods, aka, akaMethods, protocols, root: el }
|
|
192
109
|
);
|
|
193
110
|
if ('__v' in resolvedValue) {
|
|
194
111
|
// Single value — can't assign to element root without a path
|
|
@@ -205,8 +122,8 @@ export async function assignFromAsync(
|
|
|
205
122
|
await _processHandlerCommands(target, handlerKeys, expandedPattern, options, permissions);
|
|
206
123
|
}
|
|
207
124
|
// Process #[x] handler keys — resolve element, then pass to handler processing
|
|
208
|
-
if (idRefHandlerKeys.length > 0 && (options.
|
|
209
|
-
const ids = { ...options.
|
|
125
|
+
if (idRefHandlerKeys.length > 0 && (options.pin || options.at)) {
|
|
126
|
+
const ids = { ...options.pin, ...options.at };
|
|
210
127
|
const { resolveIdVariable, parseIdRef } = await import('./resolveIdRef.js');
|
|
211
128
|
_processHandlerCommands ??= (await import('./processHandlerCommands.js')).processHandlerCommands;
|
|
212
129
|
|
package/assignGingerly.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizeAliasOptions } from './getValues.js';
|
|
1
2
|
/**
|
|
2
3
|
* GUID for global instance map storage to ensure uniqueness across package versions
|
|
3
4
|
*/
|
|
@@ -561,29 +562,13 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
561
562
|
if (!target || typeof target !== 'object') {
|
|
562
563
|
return target;
|
|
563
564
|
}
|
|
564
|
-
|
|
565
|
-
const withMethodsSet = options?.withMethods
|
|
566
|
-
? options.withMethods instanceof Set
|
|
567
|
-
? options.withMethods
|
|
568
|
-
: new Set(options.withMethods)
|
|
569
|
-
: undefined;
|
|
565
|
+
const { aliasMap, withMethods: withMethodsSet } = normalizeAliasOptions(options);
|
|
570
566
|
// Convert withAsyncMethods array to Set for O(1) lookup
|
|
571
567
|
const withAsyncMethodsSet = options?.withAsyncMethods
|
|
572
568
|
? options.withAsyncMethods instanceof Set
|
|
573
569
|
? options.withAsyncMethods
|
|
574
570
|
: new Set(options.withAsyncMethods)
|
|
575
571
|
: undefined;
|
|
576
|
-
// Convert aka object to Map for O(1) lookup and validate aliases
|
|
577
|
-
const aliasMap = new Map();
|
|
578
|
-
if (options?.aka) {
|
|
579
|
-
for (const [alias, target] of Object.entries(options.aka)) {
|
|
580
|
-
// Validate: disallow space and backtick in aliases
|
|
581
|
-
if (alias.includes(' ') || alias.includes('`')) {
|
|
582
|
-
throw new Error(`Invalid alias '${alias}': aliases cannot contain space or backtick characters`);
|
|
583
|
-
}
|
|
584
|
-
aliasMap.set(alias, target);
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
572
|
const registry = options?.registry instanceof EnhancementRegistry
|
|
588
573
|
? options.registry
|
|
589
574
|
: options?.registry
|
|
@@ -653,22 +638,61 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
653
638
|
if (path) {
|
|
654
639
|
if (isNestedPath(path)) {
|
|
655
640
|
const pathParts = parsePath(path);
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
641
|
+
// Check for withMethods path evaluation
|
|
642
|
+
let lhsValue;
|
|
643
|
+
let lhsParent;
|
|
644
|
+
let lhsKey;
|
|
645
|
+
if (withMethodsSet) {
|
|
646
|
+
const result = evaluatePathWithMethods(target, pathParts, value, withMethodsSet);
|
|
647
|
+
lhsParent = result.target;
|
|
648
|
+
lhsKey = result.lastKey;
|
|
649
|
+
lhsValue = lhsParent[lhsKey];
|
|
660
650
|
}
|
|
661
|
-
else
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
651
|
+
else {
|
|
652
|
+
lhsKey = pathParts[pathParts.length - 1];
|
|
653
|
+
lhsParent = ensureNestedPath(target, pathParts);
|
|
654
|
+
lhsValue = lhsParent[lhsKey];
|
|
655
|
+
}
|
|
656
|
+
// Event handler: Element LHS + object RHS with 'on' property
|
|
657
|
+
if (lhsValue instanceof Element && value && typeof value === 'object' && !Array.isArray(value) && 'on' in value) {
|
|
658
|
+
const capturedLhs = lhsValue;
|
|
659
|
+
const capturedValue = value;
|
|
660
|
+
const capturedTarget = target;
|
|
661
|
+
const capturedOptions = options;
|
|
662
|
+
import('./handlers/addEventListener.js').then(({ attachEventListener }) => {
|
|
663
|
+
attachEventListener(capturedLhs, capturedValue, capturedTarget, capturedOptions?.from ?? capturedTarget, capturedOptions ?? {});
|
|
664
|
+
});
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
if (!(lhsKey in lhsParent)) {
|
|
668
|
+
lhsParent[lhsKey] = value;
|
|
669
|
+
}
|
|
670
|
+
else if (Array.isArray(lhsValue)) {
|
|
671
|
+
lhsParent[lhsKey] = Array.isArray(value)
|
|
672
|
+
? [...lhsValue, ...value]
|
|
673
|
+
: [...lhsValue, value];
|
|
674
|
+
}
|
|
675
|
+
else if (typeof lhsValue === 'number' && typeof value === 'string') {
|
|
676
|
+
const parsed = Number(value);
|
|
677
|
+
lhsParent[lhsKey] = isNaN(parsed) ? lhsValue + value : lhsValue + parsed;
|
|
665
678
|
}
|
|
666
679
|
else {
|
|
667
|
-
|
|
680
|
+
lhsParent[lhsKey] += value;
|
|
668
681
|
}
|
|
669
682
|
}
|
|
670
683
|
else {
|
|
671
684
|
// Plain key - direct operation on target
|
|
685
|
+
// Event handler: Element LHS + object RHS with 'on' property
|
|
686
|
+
if (target[path] instanceof Element && value && typeof value === 'object' && !Array.isArray(value) && 'on' in value) {
|
|
687
|
+
const capturedLhs = target[path];
|
|
688
|
+
const capturedValue = value;
|
|
689
|
+
const capturedTarget = target;
|
|
690
|
+
const capturedOptions = options;
|
|
691
|
+
import('./handlers/addEventListener.js').then(({ attachEventListener }) => {
|
|
692
|
+
attachEventListener(capturedLhs, capturedValue, capturedTarget, capturedOptions?.from ?? capturedTarget, capturedOptions ?? {});
|
|
693
|
+
});
|
|
694
|
+
continue;
|
|
695
|
+
}
|
|
672
696
|
if (!(path in target)) {
|
|
673
697
|
target[path] = value;
|
|
674
698
|
}
|
|
@@ -677,6 +701,10 @@ export function assignGingerly(target, source, options, permissions) {
|
|
|
677
701
|
? [...target[path], ...value]
|
|
678
702
|
: [...target[path], value];
|
|
679
703
|
}
|
|
704
|
+
else if (typeof target[path] === 'number' && typeof value === 'string') {
|
|
705
|
+
const parsed = Number(value);
|
|
706
|
+
target[path] = isNaN(parsed) ? target[path] + value : target[path] + parsed;
|
|
707
|
+
}
|
|
680
708
|
else {
|
|
681
709
|
target[path] += value;
|
|
682
710
|
}
|
package/assignGingerly.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { EnhancementConfig } from "./types/assign-gingerly/types";
|
|
4
4
|
import type { FeatureConfigsMap } from "./types/assign-gingerly/types";
|
|
5
5
|
import type { AssignPermissions } from "./isAllowedImportPath.js";
|
|
6
|
+
import { normalizeAliasOptions } from './getValues.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Constructor signature for ItemScope Manager classes
|
|
@@ -82,6 +83,13 @@ export interface IAssignGingerlyOptions {
|
|
|
82
83
|
* // Equivalent to: element.querySelector('my-element').classList.add('highlighted')
|
|
83
84
|
*/
|
|
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>;
|
|
85
93
|
|
|
86
94
|
/**
|
|
87
95
|
* AbortSignal for cleaning up reactive subscriptions (@eachTime)
|
|
@@ -760,12 +768,7 @@ export function assignGingerly(
|
|
|
760
768
|
return target;
|
|
761
769
|
}
|
|
762
770
|
|
|
763
|
-
|
|
764
|
-
const withMethodsSet = options?.withMethods
|
|
765
|
-
? options.withMethods instanceof Set
|
|
766
|
-
? options.withMethods
|
|
767
|
-
: new Set(options.withMethods)
|
|
768
|
-
: undefined;
|
|
771
|
+
const { aliasMap, withMethods: withMethodsSet } = normalizeAliasOptions(options);
|
|
769
772
|
|
|
770
773
|
// Convert withAsyncMethods array to Set for O(1) lookup
|
|
771
774
|
const withAsyncMethodsSet = options?.withAsyncMethods
|
|
@@ -774,18 +777,6 @@ export function assignGingerly(
|
|
|
774
777
|
: new Set(options.withAsyncMethods)
|
|
775
778
|
: undefined;
|
|
776
779
|
|
|
777
|
-
// Convert aka object to Map for O(1) lookup and validate aliases
|
|
778
|
-
const aliasMap = new Map<string, string>();
|
|
779
|
-
if (options?.aka) {
|
|
780
|
-
for (const [alias, target] of Object.entries(options.aka)) {
|
|
781
|
-
// Validate: disallow space and backtick in aliases
|
|
782
|
-
if (alias.includes(' ') || alias.includes('`')) {
|
|
783
|
-
throw new Error(`Invalid alias '${alias}': aliases cannot contain space or backtick characters`);
|
|
784
|
-
}
|
|
785
|
-
aliasMap.set(alias, target);
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
|
|
789
780
|
const registry = options?.registry instanceof EnhancementRegistry
|
|
790
781
|
? options.registry
|
|
791
782
|
: options?.registry
|
|
@@ -860,25 +851,69 @@ export function assignGingerly(
|
|
|
860
851
|
if (path) {
|
|
861
852
|
if (isNestedPath(path)) {
|
|
862
853
|
const pathParts = parsePath(path);
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
854
|
+
|
|
855
|
+
// Check for withMethods path evaluation
|
|
856
|
+
let lhsValue: any;
|
|
857
|
+
let lhsParent: any;
|
|
858
|
+
let lhsKey: string;
|
|
859
|
+
if (withMethodsSet) {
|
|
860
|
+
const result = evaluatePathWithMethods(target, pathParts, value, withMethodsSet);
|
|
861
|
+
lhsParent = result.target;
|
|
862
|
+
lhsKey = result.lastKey;
|
|
863
|
+
lhsValue = lhsParent[lhsKey];
|
|
871
864
|
} else {
|
|
872
|
-
|
|
865
|
+
lhsKey = pathParts[pathParts.length - 1];
|
|
866
|
+
lhsParent = ensureNestedPath(target, pathParts);
|
|
867
|
+
lhsValue = lhsParent[lhsKey];
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// Event handler: Element LHS + object RHS with 'on' property
|
|
871
|
+
if (lhsValue instanceof Element && value && typeof value === 'object' && !Array.isArray(value) && 'on' in value) {
|
|
872
|
+
const capturedLhs = lhsValue;
|
|
873
|
+
const capturedValue = value;
|
|
874
|
+
const capturedTarget = target;
|
|
875
|
+
const capturedOptions = options;
|
|
876
|
+
import('./handlers/addEventListener.js').then(({ attachEventListener }) => {
|
|
877
|
+
attachEventListener(capturedLhs, capturedValue, capturedTarget, capturedOptions?.from ?? capturedTarget, capturedOptions ?? {});
|
|
878
|
+
});
|
|
879
|
+
continue;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
if (!(lhsKey in lhsParent)) {
|
|
883
|
+
lhsParent[lhsKey] = value;
|
|
884
|
+
} else if (Array.isArray(lhsValue)) {
|
|
885
|
+
lhsParent[lhsKey] = Array.isArray(value)
|
|
886
|
+
? [...lhsValue, ...value]
|
|
887
|
+
: [...lhsValue, value];
|
|
888
|
+
} else if (typeof lhsValue === 'number' && typeof value === 'string') {
|
|
889
|
+
const parsed = Number(value);
|
|
890
|
+
lhsParent[lhsKey] = isNaN(parsed) ? lhsValue + value : lhsValue + parsed;
|
|
891
|
+
} else {
|
|
892
|
+
lhsParent[lhsKey] += value;
|
|
873
893
|
}
|
|
874
894
|
} else {
|
|
875
895
|
// Plain key - direct operation on target
|
|
896
|
+
// Event handler: Element LHS + object RHS with 'on' property
|
|
897
|
+
if (target[path] instanceof Element && value && typeof value === 'object' && !Array.isArray(value) && 'on' in value) {
|
|
898
|
+
const capturedLhs = target[path];
|
|
899
|
+
const capturedValue = value;
|
|
900
|
+
const capturedTarget = target;
|
|
901
|
+
const capturedOptions = options;
|
|
902
|
+
import('./handlers/addEventListener.js').then(({ attachEventListener }) => {
|
|
903
|
+
attachEventListener(capturedLhs, capturedValue, capturedTarget, capturedOptions?.from ?? capturedTarget, capturedOptions ?? {});
|
|
904
|
+
});
|
|
905
|
+
continue;
|
|
906
|
+
}
|
|
907
|
+
|
|
876
908
|
if (!(path in target)) {
|
|
877
909
|
target[path] = value;
|
|
878
910
|
} else if (Array.isArray(target[path])) {
|
|
879
911
|
target[path] = Array.isArray(value)
|
|
880
912
|
? [...target[path], ...value]
|
|
881
913
|
: [...target[path], value];
|
|
914
|
+
} else if (typeof target[path] === 'number' && typeof value === 'string') {
|
|
915
|
+
const parsed = Number(value);
|
|
916
|
+
target[path] = isNaN(parsed) ? target[path] + value : target[path] + parsed;
|
|
882
917
|
} else {
|
|
883
918
|
target[path] += value;
|
|
884
919
|
}
|
package/assignTentatively.js
CHANGED
|
@@ -129,6 +129,10 @@ export function assignTentatively(target, source, options) {
|
|
|
129
129
|
? [...parent[lastKey], ...value]
|
|
130
130
|
: [...parent[lastKey], value];
|
|
131
131
|
}
|
|
132
|
+
else if (typeof parent[lastKey] === 'number' && typeof value === 'string') {
|
|
133
|
+
const parsed = Number(value);
|
|
134
|
+
parent[lastKey] = isNaN(parsed) ? parent[lastKey] + value : parent[lastKey] + parsed;
|
|
135
|
+
}
|
|
132
136
|
else {
|
|
133
137
|
parent[lastKey] += value;
|
|
134
138
|
}
|
|
@@ -149,6 +153,10 @@ export function assignTentatively(target, source, options) {
|
|
|
149
153
|
? [...target[path], ...value]
|
|
150
154
|
: [...target[path], value];
|
|
151
155
|
}
|
|
156
|
+
else if (typeof target[path] === 'number' && typeof value === 'string') {
|
|
157
|
+
const parsed = Number(value);
|
|
158
|
+
target[path] = isNaN(parsed) ? target[path] + value : target[path] + parsed;
|
|
159
|
+
}
|
|
152
160
|
else {
|
|
153
161
|
target[path] += value;
|
|
154
162
|
}
|
package/assignTentatively.ts
CHANGED
|
@@ -155,6 +155,9 @@ export function assignTentatively(
|
|
|
155
155
|
parent[lastKey] = Array.isArray(value)
|
|
156
156
|
? [...parent[lastKey], ...value]
|
|
157
157
|
: [...parent[lastKey], value];
|
|
158
|
+
} else if (typeof parent[lastKey] === 'number' && typeof value === 'string') {
|
|
159
|
+
const parsed = Number(value);
|
|
160
|
+
parent[lastKey] = isNaN(parsed) ? parent[lastKey] + value : parent[lastKey] + parsed;
|
|
158
161
|
} else {
|
|
159
162
|
parent[lastKey] += value;
|
|
160
163
|
}
|
|
@@ -172,6 +175,9 @@ export function assignTentatively(
|
|
|
172
175
|
target[path] = Array.isArray(value)
|
|
173
176
|
? [...target[path], ...value]
|
|
174
177
|
: [...target[path], value];
|
|
178
|
+
} else if (typeof target[path] === 'number' && typeof value === 'string') {
|
|
179
|
+
const parsed = Number(value);
|
|
180
|
+
target[path] = isNaN(parsed) ? target[path] + value : target[path] + parsed;
|
|
175
181
|
} else {
|
|
176
182
|
target[path] += value;
|
|
177
183
|
}
|
|
@@ -1,34 +1,46 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* builtInEmoji.ts — Predefined emoji aliases for built-in handlers.
|
|
3
|
-
*
|
|
4
|
-
* Import and spread into the `handlers` option for concise handler configs:
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* import { builtInEmoji } from 'assign-gingerly/builtInEmoji.js';
|
|
8
|
-
*
|
|
9
|
-
* assignFrom(target, {
|
|
10
|
-
* '?.el =>': { do: '🔗', get: { value: ['?.first', ' ', '?.last'] } }
|
|
11
|
-
* }, { from: vm, handlers: builtInEmoji });
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* |
|
|
20
|
-
* |
|
|
21
|
-
* |
|
|
22
|
-
* |
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
'
|
|
27
|
-
'
|
|
28
|
-
'
|
|
29
|
-
'
|
|
30
|
-
'
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
1
|
+
/**
|
|
2
|
+
* builtInEmoji.ts — Predefined emoji aliases for built-in handlers.
|
|
3
|
+
*
|
|
4
|
+
* Import and spread into the `handlers` option for concise handler configs:
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* import { builtInEmoji } from 'assign-gingerly/builtInEmoji.js';
|
|
8
|
+
*
|
|
9
|
+
* assignFrom(target, {
|
|
10
|
+
* '?.el =>': { do: '🔗', get: { value: ['?.first', ' ', '?.last'] } }
|
|
11
|
+
* }, { from: vm, handlers: builtInEmoji });
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Emoji → built-in handler name mapping.
|
|
15
|
+
*
|
|
16
|
+
* | Emoji | Handler |
|
|
17
|
+
* |-------|---------|
|
|
18
|
+
* | 📦 | builtIns.lazyLoad |
|
|
19
|
+
* | 🎚️ | builtIns.lazyLoadSwitch |
|
|
20
|
+
* | 🔗 | builtIns.join |
|
|
21
|
+
* | 🏷️ | builtIns.microDataJoin |
|
|
22
|
+
* | 📋 | builtIns.manageTemplateList |
|
|
23
|
+
*/
|
|
24
|
+
export const builtInEmoji = {
|
|
25
|
+
'📦': 'builtIns.lazyLoad',
|
|
26
|
+
'🎚️': 'builtIns.lazyLoadSwitch',
|
|
27
|
+
'🔗': 'builtIns.join',
|
|
28
|
+
'🏷️': 'builtIns.microDataJoin',
|
|
29
|
+
'📋': 'builtIns.manageTemplateList',
|
|
30
|
+
'📊': 'builtIns.rangeSelector',
|
|
31
|
+
};
|
|
32
|
+
export const akaMethods = {
|
|
33
|
+
'🔍': 'querySelector',
|
|
34
|
+
'🧺': 'querySelectorAll',
|
|
35
|
+
'+': 'add',
|
|
36
|
+
'🧬': 'cloneNode'
|
|
37
|
+
};
|
|
38
|
+
export const aka = {
|
|
39
|
+
'©️': 'content?.cloneNode?.true',
|
|
40
|
+
//'🔎': 'clone?.querySelector'
|
|
41
|
+
};
|
|
42
|
+
export const emojis = {
|
|
43
|
+
builtInEmoji,
|
|
44
|
+
akaMethods,
|
|
45
|
+
};
|
|
46
|
+
export default emojis;
|
package/emojis.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* builtInEmoji.ts — Predefined emoji aliases for built-in handlers.
|
|
3
|
+
*
|
|
4
|
+
* Import and spread into the `handlers` option for concise handler configs:
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* import { builtInEmoji } from 'assign-gingerly/builtInEmoji.js';
|
|
8
|
+
*
|
|
9
|
+
* assignFrom(target, {
|
|
10
|
+
* '?.el =>': { do: '🔗', get: { value: ['?.first', ' ', '?.last'] } }
|
|
11
|
+
* }, { from: vm, handlers: builtInEmoji });
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Emoji → built-in handler name mapping.
|
|
16
|
+
*
|
|
17
|
+
* | Emoji | Handler |
|
|
18
|
+
* |-------|---------|
|
|
19
|
+
* | 📦 | builtIns.lazyLoad |
|
|
20
|
+
* | 🎚️ | builtIns.lazyLoadSwitch |
|
|
21
|
+
* | 🔗 | builtIns.join |
|
|
22
|
+
* | 🏷️ | builtIns.microDataJoin |
|
|
23
|
+
* | 📋 | builtIns.manageTemplateList |
|
|
24
|
+
*/
|
|
25
|
+
export const builtInEmoji: Record<string, string> = {
|
|
26
|
+
'📦': 'builtIns.lazyLoad',
|
|
27
|
+
'🎚️': 'builtIns.lazyLoadSwitch',
|
|
28
|
+
'🔗': 'builtIns.join',
|
|
29
|
+
'🏷️': 'builtIns.microDataJoin',
|
|
30
|
+
'📋': 'builtIns.manageTemplateList',
|
|
31
|
+
'📊': 'builtIns.rangeSelector',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const akaMethods: Record<string, string> = {
|
|
35
|
+
'🔍': 'querySelector',
|
|
36
|
+
'🧺': 'querySelectorAll',
|
|
37
|
+
'+': 'add',
|
|
38
|
+
'🧬': 'cloneNode'
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const aka: Record<string, string> = {
|
|
42
|
+
'©️': 'content?.cloneNode?.true',
|
|
43
|
+
//'🔎': 'clone?.querySelector'
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
export const emojis = {
|
|
48
|
+
builtInEmoji,
|
|
49
|
+
akaMethods,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export default emojis;
|