assign-gingerly 0.0.81 → 0.0.83
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 +2 -1
- package/DX/emojis.ts +2 -1
- package/DX/paths.js +14 -14
- package/DX/paths.ts +155 -156
- package/README.md +2 -1
- package/assignFrom.js +6 -3
- package/assignFrom.ts +6 -3
- package/assignFromAsync.js +4 -3
- package/assignFromAsync.ts +4 -3
- package/inferencer/types/NewCustomElement.md +8 -5
- package/inferencer/types/NewHTMLFirstCustomElement.md +75 -14
- package/inferencer/types/assign-gingerly/types.d.ts +21 -0
- package/inferencer/types/el-maker/types.d.ts +2 -0
- package/inferencer/types/roundabout/types.d.ts +9 -1
- package/inferencer/types/swipe-dismiss/types.d.ts +43 -0
- package/package.json +1 -1
- package/processHandlerCommands.js +2 -0
- package/processHandlerCommands.ts +2 -0
- package/resolve/getValues.js +55 -9
- package/resolve/getValues.ts +70 -9
- package/types/assign-gingerly/types.d.ts +21 -0
package/DX/emojis.js
CHANGED
package/DX/emojis.ts
CHANGED
package/DX/paths.js
CHANGED
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
* const value = sp`${$.lastName}, ${$.firstName}`;
|
|
20
20
|
* // ['?.lastName', ', ', '?.firstName']
|
|
21
21
|
*
|
|
22
|
-
* // Use .
|
|
23
|
-
* const key = $.textContent.
|
|
22
|
+
* // Use .Path for raw string contexts (object keys, plain arrays):
|
|
23
|
+
* const key = $.textContent.Path; // '?.textContent'
|
|
24
24
|
*/
|
|
25
25
|
/**
|
|
26
26
|
* Symbol used internally to detect path proxy objects.
|
|
@@ -49,7 +49,7 @@ function appendCommandSuffix(prefix, token) {
|
|
|
49
49
|
return `${prefix}${COMMAND_TOKEN_SUFFIXES[token]}`;
|
|
50
50
|
}
|
|
51
51
|
function getReservedToken(prop) {
|
|
52
|
-
if (prop === 'Path'
|
|
52
|
+
if (prop === 'Path')
|
|
53
53
|
return prop;
|
|
54
54
|
if (prop in COMMAND_TOKEN_SUFFIXES)
|
|
55
55
|
return prop;
|
|
@@ -58,14 +58,14 @@ function getReservedToken(prop) {
|
|
|
58
58
|
/**
|
|
59
59
|
* Create a proxy for id-ref paths (#[varName]).
|
|
60
60
|
* After the initial #[varName], further property access chains with ?. from the resolved element.
|
|
61
|
-
* .
|
|
61
|
+
* .Path returns the #[varName] prefix (optionally with further ?. path).
|
|
62
62
|
*/
|
|
63
63
|
function createIdRefProxy(idRef, options) {
|
|
64
64
|
function handler() { }
|
|
65
65
|
Object.defineProperty(handler, PATH_SYMBOL, { value: idRef });
|
|
66
66
|
return new Proxy(handler, {
|
|
67
67
|
get(_, prop) {
|
|
68
|
-
if (prop === '
|
|
68
|
+
if (prop === 'Path' || prop === PATH_SYMBOL) {
|
|
69
69
|
return idRef;
|
|
70
70
|
}
|
|
71
71
|
if (typeof prop === 'symbol')
|
|
@@ -74,7 +74,7 @@ function createIdRefProxy(idRef, options) {
|
|
|
74
74
|
if (reservedToken === 'Each') {
|
|
75
75
|
return createIdRefProxy(appendPathSegment(idRef, '@each'), options);
|
|
76
76
|
}
|
|
77
|
-
if (reservedToken && reservedToken !== 'Path'
|
|
77
|
+
if (reservedToken && reservedToken !== 'Path') {
|
|
78
78
|
return createIdRefProxy(appendCommandSuffix(idRef, reservedToken), options);
|
|
79
79
|
}
|
|
80
80
|
// Chain further path segments after the id ref
|
|
@@ -116,14 +116,14 @@ function createPathProxy(prefix, options) {
|
|
|
116
116
|
Object.defineProperty(handler, PATH_SYMBOL, { value: prefix.length > 0 ? `?.${prefix}` : '?.' });
|
|
117
117
|
return new Proxy(handler, {
|
|
118
118
|
get(_, prop) {
|
|
119
|
-
if (prop === '
|
|
119
|
+
if (prop === 'Path' || prop === PATH_SYMBOL) {
|
|
120
120
|
return serializePath(prefix);
|
|
121
121
|
}
|
|
122
122
|
// Ignore symbol access (Symbol.iterator, Symbol.toPrimitive, etc.)
|
|
123
123
|
if (typeof prop === 'symbol')
|
|
124
124
|
return undefined;
|
|
125
125
|
const reservedToken = getReservedToken(String(prop));
|
|
126
|
-
if (reservedToken === 'Path'
|
|
126
|
+
if (reservedToken === 'Path') {
|
|
127
127
|
return serializePath(prefix);
|
|
128
128
|
}
|
|
129
129
|
if (reservedToken === 'Each') {
|
|
@@ -185,21 +185,21 @@ function createPathProxy(prefix, options) {
|
|
|
185
185
|
*
|
|
186
186
|
* @example
|
|
187
187
|
* const $ = paths<Person>();
|
|
188
|
-
* $.lastName.
|
|
189
|
-
* $.address.city.
|
|
188
|
+
* $.lastName.Path // '?.lastName'
|
|
189
|
+
* $.address.city.Path // '?.address?.city'
|
|
190
190
|
*
|
|
191
191
|
* // With aka (reverse alias applied):
|
|
192
192
|
* const $ = paths<MyEl>({ aka: { q: 'querySelector' } });
|
|
193
|
-
* $.querySelector('.user').textContent.
|
|
193
|
+
* $.querySelector('.user').textContent.Path // '?.q?..user?.textContent'
|
|
194
194
|
*
|
|
195
|
-
* // Inside sp template literals, .
|
|
195
|
+
* // Inside sp template literals, .Path is not needed:
|
|
196
196
|
* sp`${$.lastName}, ${$.firstName}` // ['?.lastName', ', ', '?.firstName']
|
|
197
197
|
*/
|
|
198
198
|
export function paths(options) {
|
|
199
199
|
return createPathProxy('', options);
|
|
200
200
|
}
|
|
201
201
|
/**
|
|
202
|
-
* Create an assignment pair: { [lhs.
|
|
202
|
+
* Create an assignment pair: { [lhs.Path]: rhs.Path }.
|
|
203
203
|
* Used to express "set this target to this source value" in a spreadable form.
|
|
204
204
|
*
|
|
205
205
|
* @example
|
|
@@ -318,7 +318,7 @@ export function forEachKeyIn(keys, factory, options) {
|
|
|
318
318
|
* Interleaves static string segments with interpolated values.
|
|
319
319
|
*
|
|
320
320
|
* Path proxy objects are auto-detected and converted to their `?.`-prefixed
|
|
321
|
-
* string representation — no `.
|
|
321
|
+
* string representation — no `.Path` call needed inside sp template literals.
|
|
322
322
|
*
|
|
323
323
|
* Arrays passed as interpolations are preserved as nested arrays (for
|
|
324
324
|
* all-or-nothing optional segments in builtIns.join).
|
package/DX/paths.ts
CHANGED
|
@@ -19,76 +19,75 @@
|
|
|
19
19
|
* const value = sp`${$.lastName}, ${$.firstName}`;
|
|
20
20
|
* // ['?.lastName', ', ', '?.firstName']
|
|
21
21
|
*
|
|
22
|
-
* // Use .
|
|
23
|
-
* const key = $.textContent.
|
|
22
|
+
* // Use .Path for raw string contexts (object keys, plain arrays):
|
|
23
|
+
* const key = $.textContent.Path; // '?.textContent'
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Symbol used internally to detect path proxy objects.
|
|
28
28
|
* The sp tag function uses this to auto-extract path strings from proxies.
|
|
29
29
|
*/
|
|
30
|
-
const PATH_SYMBOL = Symbol('assign-gingerly-path');
|
|
31
|
-
|
|
32
|
-
function isPathProxy(value: unknown): value is { [PATH_SYMBOL]: string } {
|
|
33
|
-
return !!value && (typeof value === 'object' || typeof value === 'function') && PATH_SYMBOL in value;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const COMMAND_TOKEN_SUFFIXES = {
|
|
37
|
-
Each: '?.@each',
|
|
38
|
-
EqNot: ' =!',
|
|
39
|
-
PlusEq: ' +=',
|
|
40
|
-
QMEq: ' ?=',
|
|
41
|
-
YEq: ' Y=',
|
|
42
|
-
MinusEq: ' -=',
|
|
43
|
-
Arrow: ' =>',
|
|
44
|
-
} as const;
|
|
45
|
-
|
|
46
|
-
type CommandToken = keyof typeof COMMAND_TOKEN_SUFFIXES;
|
|
47
|
-
|
|
48
|
-
function serializePath(prefix: string): string {
|
|
49
|
-
return prefix.length > 0 ? `?.${prefix}` : '?.';
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function appendPathSegment(prefix: string, segment: string): string {
|
|
53
|
-
return prefix ? `${prefix}?.${segment}` : segment;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function appendCommandSuffix(prefix: string, token: CommandToken): string {
|
|
57
|
-
return `${prefix}${COMMAND_TOKEN_SUFFIXES[token]}`;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function getReservedToken(prop: string): CommandToken | 'Path' |
|
|
61
|
-
if (prop === 'Path'
|
|
62
|
-
if (prop in COMMAND_TOKEN_SUFFIXES) return prop as CommandToken;
|
|
63
|
-
return undefined;
|
|
64
|
-
}
|
|
30
|
+
const PATH_SYMBOL = Symbol('assign-gingerly-path');
|
|
31
|
+
|
|
32
|
+
function isPathProxy(value: unknown): value is { [PATH_SYMBOL]: string } {
|
|
33
|
+
return !!value && (typeof value === 'object' || typeof value === 'function') && PATH_SYMBOL in value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const COMMAND_TOKEN_SUFFIXES = {
|
|
37
|
+
Each: '?.@each',
|
|
38
|
+
EqNot: ' =!',
|
|
39
|
+
PlusEq: ' +=',
|
|
40
|
+
QMEq: ' ?=',
|
|
41
|
+
YEq: ' Y=',
|
|
42
|
+
MinusEq: ' -=',
|
|
43
|
+
Arrow: ' =>',
|
|
44
|
+
} as const;
|
|
45
|
+
|
|
46
|
+
type CommandToken = keyof typeof COMMAND_TOKEN_SUFFIXES;
|
|
47
|
+
|
|
48
|
+
function serializePath(prefix: string): string {
|
|
49
|
+
return prefix.length > 0 ? `?.${prefix}` : '?.';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function appendPathSegment(prefix: string, segment: string): string {
|
|
53
|
+
return prefix ? `${prefix}?.${segment}` : segment;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function appendCommandSuffix(prefix: string, token: CommandToken): string {
|
|
57
|
+
return `${prefix}${COMMAND_TOKEN_SUFFIXES[token]}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function getReservedToken(prop: string): CommandToken | 'Path' | undefined {
|
|
61
|
+
if (prop === 'Path') return prop;
|
|
62
|
+
if (prop in COMMAND_TOKEN_SUFFIXES) return prop as CommandToken;
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
|
-
* Type that maps an object type to a proxy where every property access
|
|
68
|
-
* returns either a deeper proxy (for object properties) or a terminal
|
|
69
|
-
* with a serialized path string accessor — while providing full autocomplete.
|
|
70
|
-
* Enhanced: also callable (for method call syntax) and includes reserved
|
|
71
|
-
* command markers such as `Each`, `EqNot`, and `PlusEq`.
|
|
72
|
-
*/
|
|
73
|
-
export type PathProxyCore = {
|
|
74
|
-
readonly Path: string;
|
|
75
|
-
readonly
|
|
76
|
-
readonly
|
|
77
|
-
readonly
|
|
78
|
-
readonly
|
|
79
|
-
readonly
|
|
80
|
-
readonly
|
|
81
|
-
readonly
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
} & PathProxyCore & ((...args: any[]) => PathProxy<any> & PathProxyCore);
|
|
67
|
+
* Type that maps an object type to a proxy where every property access
|
|
68
|
+
* returns either a deeper proxy (for object properties) or a terminal
|
|
69
|
+
* with a serialized path string accessor — while providing full autocomplete.
|
|
70
|
+
* Enhanced: also callable (for method call syntax) and includes reserved
|
|
71
|
+
* command markers such as `Each`, `EqNot`, and `PlusEq`.
|
|
72
|
+
*/
|
|
73
|
+
export type PathProxyCore = {
|
|
74
|
+
readonly Path: string;
|
|
75
|
+
readonly Each: PathProxy<any>;
|
|
76
|
+
readonly EqNot: PathProxy<any>;
|
|
77
|
+
readonly PlusEq: PathProxy<any>;
|
|
78
|
+
readonly QMEq: PathProxy<any>;
|
|
79
|
+
readonly YEq: PathProxy<any>;
|
|
80
|
+
readonly MinusEq: PathProxy<any>;
|
|
81
|
+
readonly Arrow: PathProxy<any>;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export type PathProxy<T> = {
|
|
85
|
+
[K in keyof T]-?: T[K] extends ((...args: any[]) => infer R)
|
|
86
|
+
? ((...args: any[]) => PathProxy<NonNullable<R>> & PathProxyCore) & PathProxy<NonNullable<R>> & PathProxyCore
|
|
87
|
+
: T[K] extends (object | undefined | null)
|
|
88
|
+
? PathProxy<NonNullable<T[K]>> & PathProxyCore & ((...args: any[]) => PathProxy<any> & PathProxyCore)
|
|
89
|
+
: PathProxyCore & ((...args: any[]) => PathProxy<any> & PathProxyCore);
|
|
90
|
+
} & PathProxyCore & ((...args: any[]) => PathProxy<any> & PathProxyCore);
|
|
92
91
|
|
|
93
92
|
/**
|
|
94
93
|
* Options for paths proxy creation.
|
|
@@ -103,33 +102,33 @@ export interface PathsOptions {
|
|
|
103
102
|
/**
|
|
104
103
|
* Create a proxy for id-ref paths (#[varName]).
|
|
105
104
|
* After the initial #[varName], further property access chains with ?. from the resolved element.
|
|
106
|
-
* .
|
|
105
|
+
* .Path returns the #[varName] prefix (optionally with further ?. path).
|
|
107
106
|
*/
|
|
108
|
-
function createIdRefProxy(idRef: string, options?: PathsOptions): any {
|
|
109
|
-
function handler() {}
|
|
110
|
-
Object.defineProperty(handler, PATH_SYMBOL, { value: idRef });
|
|
111
|
-
return new Proxy(handler, {
|
|
112
|
-
get(_, prop: string | symbol) {
|
|
113
|
-
if (prop === '
|
|
107
|
+
function createIdRefProxy(idRef: string, options?: PathsOptions): any {
|
|
108
|
+
function handler() {}
|
|
109
|
+
Object.defineProperty(handler, PATH_SYMBOL, { value: idRef });
|
|
110
|
+
return new Proxy(handler, {
|
|
111
|
+
get(_, prop: string | symbol) {
|
|
112
|
+
if (prop === 'Path' || prop === PATH_SYMBOL) {
|
|
114
113
|
return idRef;
|
|
115
114
|
}
|
|
116
|
-
if (typeof prop === 'symbol') return undefined;
|
|
117
|
-
|
|
118
|
-
const reservedToken = getReservedToken(prop);
|
|
119
|
-
if (reservedToken === 'Each') {
|
|
120
|
-
return createIdRefProxy(appendPathSegment(idRef, '@each'), options);
|
|
121
|
-
}
|
|
122
|
-
if (reservedToken && reservedToken !== 'Path'
|
|
115
|
+
if (typeof prop === 'symbol') return undefined;
|
|
116
|
+
|
|
117
|
+
const reservedToken = getReservedToken(prop);
|
|
118
|
+
if (reservedToken === 'Each') {
|
|
119
|
+
return createIdRefProxy(appendPathSegment(idRef, '@each'), options);
|
|
120
|
+
}
|
|
121
|
+
if (reservedToken && reservedToken !== 'Path') {
|
|
123
122
|
return createIdRefProxy(appendCommandSuffix(idRef, reservedToken), options);
|
|
124
123
|
}
|
|
125
|
-
|
|
126
|
-
// Chain further path segments after the id ref
|
|
127
|
-
const chained = appendPathSegment(idRef, String(prop));
|
|
128
|
-
return createIdRefProxy(chained, options);
|
|
129
|
-
},
|
|
130
|
-
apply(_, __, args) {
|
|
131
|
-
if (args.length > 0) {
|
|
132
|
-
const arg = args[0];
|
|
124
|
+
|
|
125
|
+
// Chain further path segments after the id ref
|
|
126
|
+
const chained = appendPathSegment(idRef, String(prop));
|
|
127
|
+
return createIdRefProxy(chained, options);
|
|
128
|
+
},
|
|
129
|
+
apply(_, __, args) {
|
|
130
|
+
if (args.length > 0) {
|
|
131
|
+
const arg = args[0];
|
|
133
132
|
let argStr: string;
|
|
134
133
|
if (arg === true) argStr = 'true';
|
|
135
134
|
else if (arg === false) argStr = 'false';
|
|
@@ -154,72 +153,72 @@ function createIdRefProxy(idRef: string, options?: PathsOptions): any {
|
|
|
154
153
|
* When `aka` is provided, property names that match an alias *value* are output
|
|
155
154
|
* using the alias *key* instead (reverse alias).
|
|
156
155
|
*/
|
|
157
|
-
function createPathProxy(prefix: string, options?: PathsOptions): any {
|
|
158
|
-
const aliasMap = options?.aka;
|
|
159
|
-
|
|
160
|
-
// Use a function as the target to enable the apply trap
|
|
161
|
-
function handler() {}
|
|
162
|
-
Object.defineProperty(handler, PATH_SYMBOL, { value: prefix.length > 0 ? `?.${prefix}` : '?.' });
|
|
163
|
-
|
|
164
|
-
return new Proxy(handler, {
|
|
165
|
-
get(_, prop: string | symbol) {
|
|
166
|
-
if (prop === '
|
|
156
|
+
function createPathProxy(prefix: string, options?: PathsOptions): any {
|
|
157
|
+
const aliasMap = options?.aka;
|
|
158
|
+
|
|
159
|
+
// Use a function as the target to enable the apply trap
|
|
160
|
+
function handler() {}
|
|
161
|
+
Object.defineProperty(handler, PATH_SYMBOL, { value: prefix.length > 0 ? `?.${prefix}` : '?.' });
|
|
162
|
+
|
|
163
|
+
return new Proxy(handler, {
|
|
164
|
+
get(_, prop: string | symbol) {
|
|
165
|
+
if (prop === 'Path' || prop === PATH_SYMBOL) {
|
|
167
166
|
return serializePath(prefix);
|
|
168
167
|
}
|
|
169
|
-
// Ignore symbol access (Symbol.iterator, Symbol.toPrimitive, etc.)
|
|
170
|
-
if (typeof prop === 'symbol') return undefined;
|
|
171
|
-
|
|
172
|
-
const reservedToken = getReservedToken(String(prop));
|
|
173
|
-
if (reservedToken === 'Path'
|
|
168
|
+
// Ignore symbol access (Symbol.iterator, Symbol.toPrimitive, etc.)
|
|
169
|
+
if (typeof prop === 'symbol') return undefined;
|
|
170
|
+
|
|
171
|
+
const reservedToken = getReservedToken(String(prop));
|
|
172
|
+
if (reservedToken === 'Path') {
|
|
174
173
|
return serializePath(prefix);
|
|
175
174
|
}
|
|
176
|
-
if (reservedToken === 'Each') {
|
|
177
|
-
return createPathProxy(appendPathSegment(prefix, '@each'), options);
|
|
178
|
-
}
|
|
179
|
-
if (reservedToken) {
|
|
180
|
-
return createPathProxy(appendCommandSuffix(prefix, reservedToken), options);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
let segment = String(prop);
|
|
184
|
-
|
|
185
|
-
// #-prefix: $['#firstName'] → '#[firstName]' (cached element ref)
|
|
186
|
-
if (segment.startsWith('#')) {
|
|
187
|
-
const varName = segment.substring(1);
|
|
188
|
-
const idRef = `#[${varName}]`;
|
|
175
|
+
if (reservedToken === 'Each') {
|
|
176
|
+
return createPathProxy(appendPathSegment(prefix, '@each'), options);
|
|
177
|
+
}
|
|
178
|
+
if (reservedToken) {
|
|
179
|
+
return createPathProxy(appendCommandSuffix(prefix, reservedToken), options);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
let segment = String(prop);
|
|
183
|
+
|
|
184
|
+
// #-prefix: $['#firstName'] → '#[firstName]' (cached element ref)
|
|
185
|
+
if (segment.startsWith('#')) {
|
|
186
|
+
const varName = segment.substring(1);
|
|
187
|
+
const idRef = `#[${varName}]`;
|
|
189
188
|
// Return a proxy that starts from this id ref (can chain further with ?.)
|
|
190
189
|
return createIdRefProxy(idRef, options);
|
|
191
190
|
}
|
|
192
191
|
|
|
193
192
|
// Apply reverse alias: if prop matches an alias value, use the alias key
|
|
194
|
-
if (aliasMap) {
|
|
195
|
-
for (const [alias, target] of Object.entries(aliasMap)) {
|
|
196
|
-
if (target === segment) { segment = alias; break; }
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
const newPath = appendPathSegment(prefix, segment);
|
|
201
|
-
return createPathProxy(newPath, options);
|
|
202
|
-
},
|
|
203
|
-
apply(_, __, args) {
|
|
204
|
-
// Method call syntax: $.querySelector('.username') → extends path with the argument
|
|
205
|
-
if (args.length > 0) {
|
|
193
|
+
if (aliasMap) {
|
|
194
|
+
for (const [alias, target] of Object.entries(aliasMap)) {
|
|
195
|
+
if (target === segment) { segment = alias; break; }
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const newPath = appendPathSegment(prefix, segment);
|
|
200
|
+
return createPathProxy(newPath, options);
|
|
201
|
+
},
|
|
202
|
+
apply(_, __, args) {
|
|
203
|
+
// Method call syntax: $.querySelector('.username') → extends path with the argument
|
|
204
|
+
if (args.length > 0) {
|
|
206
205
|
const arg = args[0];
|
|
207
206
|
let argStr: string;
|
|
208
207
|
if (arg === true) argStr = 'true';
|
|
209
208
|
else if (arg === false) argStr = 'false';
|
|
210
|
-
else if (isPathProxy(arg)) {
|
|
211
|
-
// Proxy arg — extract path without '?.' prefix
|
|
212
|
-
const fullPath = arg[PATH_SYMBOL] as string;
|
|
213
|
-
argStr = fullPath.startsWith('?.') ? fullPath.substring(2) : fullPath;
|
|
214
|
-
}
|
|
215
|
-
else argStr = String(arg);
|
|
216
|
-
|
|
217
|
-
const newPath = appendPathSegment(prefix, argStr);
|
|
218
|
-
return createPathProxy(newPath, options);
|
|
219
|
-
}
|
|
220
|
-
// No args — method called with no arguments, return self
|
|
221
|
-
return createPathProxy(prefix, options);
|
|
222
|
-
}
|
|
209
|
+
else if (isPathProxy(arg)) {
|
|
210
|
+
// Proxy arg — extract path without '?.' prefix
|
|
211
|
+
const fullPath = arg[PATH_SYMBOL] as string;
|
|
212
|
+
argStr = fullPath.startsWith('?.') ? fullPath.substring(2) : fullPath;
|
|
213
|
+
}
|
|
214
|
+
else argStr = String(arg);
|
|
215
|
+
|
|
216
|
+
const newPath = appendPathSegment(prefix, argStr);
|
|
217
|
+
return createPathProxy(newPath, options);
|
|
218
|
+
}
|
|
219
|
+
// No args — method called with no arguments, return self
|
|
220
|
+
return createPathProxy(prefix, options);
|
|
221
|
+
}
|
|
223
222
|
});
|
|
224
223
|
}
|
|
225
224
|
|
|
@@ -232,14 +231,14 @@ function createPathProxy(prefix: string, options?: PathsOptions): any {
|
|
|
232
231
|
*
|
|
233
232
|
* @example
|
|
234
233
|
* const $ = paths<Person>();
|
|
235
|
-
* $.lastName.
|
|
236
|
-
* $.address.city.
|
|
234
|
+
* $.lastName.Path // '?.lastName'
|
|
235
|
+
* $.address.city.Path // '?.address?.city'
|
|
237
236
|
*
|
|
238
237
|
* // With aka (reverse alias applied):
|
|
239
238
|
* const $ = paths<MyEl>({ aka: { q: 'querySelector' } });
|
|
240
|
-
* $.querySelector('.user').textContent.
|
|
239
|
+
* $.querySelector('.user').textContent.Path // '?.q?..user?.textContent'
|
|
241
240
|
*
|
|
242
|
-
* // Inside sp template literals, .
|
|
241
|
+
* // Inside sp template literals, .Path is not needed:
|
|
243
242
|
* sp`${$.lastName}, ${$.firstName}` // ['?.lastName', ', ', '?.firstName']
|
|
244
243
|
*/
|
|
245
244
|
export function paths<T>(options?: PathsOptions): PathProxy<T> {
|
|
@@ -247,7 +246,7 @@ export function paths<T>(options?: PathsOptions): PathProxy<T> {
|
|
|
247
246
|
}
|
|
248
247
|
|
|
249
248
|
/**
|
|
250
|
-
* Create an assignment pair: { [lhs.
|
|
249
|
+
* Create an assignment pair: { [lhs.Path]: rhs.Path }.
|
|
251
250
|
* Used to express "set this target to this source value" in a spreadable form.
|
|
252
251
|
*
|
|
253
252
|
* @example
|
|
@@ -261,10 +260,10 @@ export function paths<T>(options?: PathsOptions): PathProxy<T> {
|
|
|
261
260
|
* count: 1
|
|
262
261
|
* }
|
|
263
262
|
*/
|
|
264
|
-
export function set(lhs: any): { to: (rhs: any) => Record<string, any> } {
|
|
265
|
-
const lhsStr = isPathProxy(lhs)
|
|
266
|
-
? lhs[PATH_SYMBOL]
|
|
267
|
-
: String(lhs);
|
|
263
|
+
export function set(lhs: any): { to: (rhs: any) => Record<string, any> } {
|
|
264
|
+
const lhsStr = isPathProxy(lhs)
|
|
265
|
+
? lhs[PATH_SYMBOL]
|
|
266
|
+
: String(lhs);
|
|
268
267
|
return {
|
|
269
268
|
to(rhs: any): Record<string, any> {
|
|
270
269
|
const rhsStr = isPathProxy(rhs)
|
|
@@ -293,9 +292,9 @@ export function set(lhs: any): { to: (rhs: any) => Record<string, any> } {
|
|
|
293
292
|
* });
|
|
294
293
|
* // { assign: { incrementButton: '?.clone?.q?..increment', ... } }
|
|
295
294
|
*/
|
|
296
|
-
export function smoothOver(value: any): any {
|
|
297
|
-
if (isPathProxy(value)) {
|
|
298
|
-
return value[PATH_SYMBOL];
|
|
295
|
+
export function smoothOver(value: any): any {
|
|
296
|
+
if (isPathProxy(value)) {
|
|
297
|
+
return value[PATH_SYMBOL];
|
|
299
298
|
}
|
|
300
299
|
if (Array.isArray(value)) {
|
|
301
300
|
return value.map(smoothOver);
|
|
@@ -374,7 +373,7 @@ export function forEachKeyIn<T>(
|
|
|
374
373
|
* Interleaves static string segments with interpolated values.
|
|
375
374
|
*
|
|
376
375
|
* Path proxy objects are auto-detected and converted to their `?.`-prefixed
|
|
377
|
-
* string representation — no `.
|
|
376
|
+
* string representation — no `.Path` call needed inside sp template literals.
|
|
378
377
|
*
|
|
379
378
|
* Arrays passed as interpolations are preserved as nested arrays (for
|
|
380
379
|
* all-or-nothing optional segments in builtIns.join).
|
|
@@ -416,12 +415,12 @@ export function sp(strings: TemplateStringsArray, ...values: any[]): any[] {
|
|
|
416
415
|
* Extract the last segment from a `?.`-prefixed path string.
|
|
417
416
|
* e.g., '?.address?.city' → 'city', '?.firstName' → 'firstName'
|
|
418
417
|
*/
|
|
419
|
-
function extractPropName(pathStr: string): string {
|
|
420
|
-
const withoutCommand = pathStr.replace(/(?: \+=| =!| \?=| Y=| -=| =>)$/, '');
|
|
421
|
-
const parts = withoutCommand.split('?.');
|
|
422
|
-
const last = parts[parts.length - 1];
|
|
423
|
-
return last === '@each' ? 'Each' : last;
|
|
424
|
-
}
|
|
418
|
+
function extractPropName(pathStr: string): string {
|
|
419
|
+
const withoutCommand = pathStr.replace(/(?: \+=| =!| \?=| Y=| -=| =>)$/, '');
|
|
420
|
+
const parts = withoutCommand.split('?.');
|
|
421
|
+
const last = parts[parts.length - 1];
|
|
422
|
+
return last === '@each' ? 'Each' : last;
|
|
423
|
+
}
|
|
425
424
|
|
|
426
425
|
/**
|
|
427
426
|
* Tagged template literal that produces an array of {prop, val} objects + literal strings.
|
package/README.md
CHANGED
|
@@ -100,7 +100,8 @@ assignFrom adds support for:
|
|
|
100
100
|
3. Protocol resolution (`globalThis://`, `localStorage://`, custom sync protocols).
|
|
101
101
|
4. Handler plugins via the ` =>` operator for custom logic (fire-and-forget in sync mode, awaitable in async mode).
|
|
102
102
|
5. Looped substitution with `where_x_in` / `where_y_in` / `where_z_in` for expanding template patterns into multiple concrete assignments.
|
|
103
|
-
6.
|
|
103
|
+
6. Dynamic substitutions via the `substitutions` option for injecting runtime string values into path segments. See [docs/substitutions.md](docs/substitutions.md).
|
|
104
|
+
7. Spread merging via the `"..."` key.
|
|
104
105
|
|
|
105
106
|
Example:
|
|
106
107
|
|
package/assignFrom.js
CHANGED
|
@@ -49,6 +49,7 @@ function resolveTernaryValue(value, source, options) {
|
|
|
49
49
|
return getValue(value, source, {
|
|
50
50
|
withMethods: options.withMethods,
|
|
51
51
|
aka: options.aka,
|
|
52
|
+
substitutions: options.substitutions,
|
|
52
53
|
protocols: options.protocols,
|
|
53
54
|
root: options.root
|
|
54
55
|
});
|
|
@@ -61,6 +62,7 @@ function resolveTernaryValue(value, source, options) {
|
|
|
61
62
|
return getValue(value, source, {
|
|
62
63
|
withMethods: options.withMethods,
|
|
63
64
|
aka: options.aka,
|
|
65
|
+
substitutions: options.substitutions,
|
|
64
66
|
protocols: options.protocols,
|
|
65
67
|
root: options.root
|
|
66
68
|
});
|
|
@@ -381,7 +383,7 @@ function processIdRefNormalKeys(idRefNormalKeys, expandedPattern, target, option
|
|
|
381
383
|
const ids = getEffectiveIds(options);
|
|
382
384
|
if (!ids)
|
|
383
385
|
return;
|
|
384
|
-
const { withMethods, aka, akaMethods, protocols, from } = options;
|
|
386
|
+
const { withMethods, aka, akaMethods, protocols, from, substitutions } = options;
|
|
385
387
|
for (const key of idRefNormalKeys) {
|
|
386
388
|
const parsed = parseIdRef(key);
|
|
387
389
|
if (!parsed)
|
|
@@ -391,11 +393,11 @@ function processIdRefNormalKeys(idRefNormalKeys, expandedPattern, target, option
|
|
|
391
393
|
continue;
|
|
392
394
|
const value = expandedPattern[key];
|
|
393
395
|
if (parsed.remainingPath) {
|
|
394
|
-
const resolvedValue = getValues({ __v: value }, from, { withMethods, aka, akaMethods, protocols, root: target, permissionProcessor });
|
|
396
|
+
const resolvedValue = getValues({ __v: value }, from, { withMethods, aka, akaMethods, substitutions, protocols, root: target, permissionProcessor });
|
|
395
397
|
assignGingerly(el, { [parsed.remainingPath]: resolvedValue.__v }, options, permissionProcessor);
|
|
396
398
|
}
|
|
397
399
|
else {
|
|
398
|
-
const resolvedValue = getValues(typeof value === 'object' && value !== null ? value : { __v: value }, from, { withMethods, aka, akaMethods, protocols, root: target, permissionProcessor });
|
|
400
|
+
const resolvedValue = getValues(typeof value === 'object' && value !== null ? value : { __v: value }, from, { withMethods, aka, akaMethods, substitutions, protocols, root: target, permissionProcessor });
|
|
399
401
|
if (!('__v' in resolvedValue)) {
|
|
400
402
|
assignGingerly(el, resolvedValue, options, permissionProcessor);
|
|
401
403
|
}
|
|
@@ -458,6 +460,7 @@ export function assignFrom(target, pattern, options, permissionProcessor) {
|
|
|
458
460
|
withMethods: options.withMethods,
|
|
459
461
|
aka: options.aka,
|
|
460
462
|
akaMethods: options.akaMethods,
|
|
463
|
+
substitutions: options.substitutions,
|
|
461
464
|
protocols: options.protocols,
|
|
462
465
|
root: target,
|
|
463
466
|
permissionProcessor
|
package/assignFrom.ts
CHANGED
|
@@ -58,6 +58,7 @@ function resolveTernaryValue(value: any, source: any, options: AssignFromOptions
|
|
|
58
58
|
return getValue(value, source, {
|
|
59
59
|
withMethods: options.withMethods,
|
|
60
60
|
aka: options.aka,
|
|
61
|
+
substitutions: options.substitutions,
|
|
61
62
|
protocols: options.protocols,
|
|
62
63
|
root: options.root
|
|
63
64
|
});
|
|
@@ -70,6 +71,7 @@ function resolveTernaryValue(value: any, source: any, options: AssignFromOptions
|
|
|
70
71
|
return getValue(value, source, {
|
|
71
72
|
withMethods: options.withMethods,
|
|
72
73
|
aka: options.aka,
|
|
74
|
+
substitutions: options.substitutions,
|
|
73
75
|
protocols: options.protocols,
|
|
74
76
|
root: options.root
|
|
75
77
|
});
|
|
@@ -395,7 +397,7 @@ function processIdRefNormalKeys(
|
|
|
395
397
|
const ids = getEffectiveIds(options);
|
|
396
398
|
if (!ids) return;
|
|
397
399
|
|
|
398
|
-
const { withMethods, aka, akaMethods, protocols, from } = options;
|
|
400
|
+
const { withMethods, aka, akaMethods, protocols, from, substitutions } = options;
|
|
399
401
|
for (const key of idRefNormalKeys) {
|
|
400
402
|
const parsed = parseIdRef(key);
|
|
401
403
|
if (!parsed) continue;
|
|
@@ -407,14 +409,14 @@ function processIdRefNormalKeys(
|
|
|
407
409
|
if (parsed.remainingPath) {
|
|
408
410
|
const resolvedValue = getValues(
|
|
409
411
|
{ __v: value }, from,
|
|
410
|
-
{ withMethods, aka, akaMethods, protocols, root: target, permissionProcessor }
|
|
412
|
+
{ withMethods, aka, akaMethods, substitutions, protocols, root: target, permissionProcessor }
|
|
411
413
|
);
|
|
412
414
|
assignGingerly(el, { [parsed.remainingPath]: resolvedValue.__v }, options, permissionProcessor);
|
|
413
415
|
} else {
|
|
414
416
|
const resolvedValue = getValues(
|
|
415
417
|
typeof value === 'object' && value !== null ? value : { __v: value },
|
|
416
418
|
from,
|
|
417
|
-
{ withMethods, aka, akaMethods, protocols, root: target, permissionProcessor }
|
|
419
|
+
{ withMethods, aka, akaMethods, substitutions, protocols, root: target, permissionProcessor }
|
|
418
420
|
);
|
|
419
421
|
if (!('__v' in resolvedValue)) {
|
|
420
422
|
assignGingerly(el, resolvedValue, options, permissionProcessor);
|
|
@@ -486,6 +488,7 @@ export function assignFrom(
|
|
|
486
488
|
withMethods: options.withMethods,
|
|
487
489
|
aka: options.aka,
|
|
488
490
|
akaMethods: options.akaMethods,
|
|
491
|
+
substitutions: options.substitutions,
|
|
489
492
|
protocols: options.protocols,
|
|
490
493
|
root: target,
|
|
491
494
|
permissionProcessor
|