assign-gingerly 0.0.80 → 0.0.82
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 +61 -17
- package/DX/paths.ts +179 -116
- package/README.md +9 -8
- package/inferencer/types/NewCustomElement.md +28 -445
- package/inferencer/types/NewHTMLFirstCustomElement.md +466 -0
- package/inferencer/types/NewJSFirstCustomElement.md +278 -0
- package/inferencer/types/roundabout/types.d.ts +2 -1
- package/package.json +1 -1
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.
|
|
@@ -30,23 +30,55 @@ const PATH_SYMBOL = Symbol('assign-gingerly-path');
|
|
|
30
30
|
function isPathProxy(value) {
|
|
31
31
|
return !!value && (typeof value === 'object' || typeof value === 'function') && PATH_SYMBOL in value;
|
|
32
32
|
}
|
|
33
|
+
const COMMAND_TOKEN_SUFFIXES = {
|
|
34
|
+
Each: '?.@each',
|
|
35
|
+
EqNot: ' =!',
|
|
36
|
+
PlusEq: ' +=',
|
|
37
|
+
QMEq: ' ?=',
|
|
38
|
+
YEq: ' Y=',
|
|
39
|
+
MinusEq: ' -=',
|
|
40
|
+
Arrow: ' =>',
|
|
41
|
+
};
|
|
42
|
+
function serializePath(prefix) {
|
|
43
|
+
return prefix.length > 0 ? `?.${prefix}` : '?.';
|
|
44
|
+
}
|
|
45
|
+
function appendPathSegment(prefix, segment) {
|
|
46
|
+
return prefix ? `${prefix}?.${segment}` : segment;
|
|
47
|
+
}
|
|
48
|
+
function appendCommandSuffix(prefix, token) {
|
|
49
|
+
return `${prefix}${COMMAND_TOKEN_SUFFIXES[token]}`;
|
|
50
|
+
}
|
|
51
|
+
function getReservedToken(prop) {
|
|
52
|
+
if (prop === 'Path')
|
|
53
|
+
return prop;
|
|
54
|
+
if (prop in COMMAND_TOKEN_SUFFIXES)
|
|
55
|
+
return prop;
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
33
58
|
/**
|
|
34
59
|
* Create a proxy for id-ref paths (#[varName]).
|
|
35
60
|
* After the initial #[varName], further property access chains with ?. from the resolved element.
|
|
36
|
-
* .
|
|
61
|
+
* .Path returns the #[varName] prefix (optionally with further ?. path).
|
|
37
62
|
*/
|
|
38
63
|
function createIdRefProxy(idRef, options) {
|
|
39
64
|
function handler() { }
|
|
40
65
|
Object.defineProperty(handler, PATH_SYMBOL, { value: idRef });
|
|
41
66
|
return new Proxy(handler, {
|
|
42
67
|
get(_, prop) {
|
|
43
|
-
if (prop === '
|
|
68
|
+
if (prop === 'Path' || prop === PATH_SYMBOL) {
|
|
44
69
|
return idRef;
|
|
45
70
|
}
|
|
46
71
|
if (typeof prop === 'symbol')
|
|
47
72
|
return undefined;
|
|
73
|
+
const reservedToken = getReservedToken(prop);
|
|
74
|
+
if (reservedToken === 'Each') {
|
|
75
|
+
return createIdRefProxy(appendPathSegment(idRef, '@each'), options);
|
|
76
|
+
}
|
|
77
|
+
if (reservedToken && reservedToken !== 'Path') {
|
|
78
|
+
return createIdRefProxy(appendCommandSuffix(idRef, reservedToken), options);
|
|
79
|
+
}
|
|
48
80
|
// Chain further path segments after the id ref
|
|
49
|
-
const chained =
|
|
81
|
+
const chained = appendPathSegment(idRef, String(prop));
|
|
50
82
|
return createIdRefProxy(chained, options);
|
|
51
83
|
},
|
|
52
84
|
apply(_, __, args) {
|
|
@@ -84,12 +116,22 @@ function createPathProxy(prefix, options) {
|
|
|
84
116
|
Object.defineProperty(handler, PATH_SYMBOL, { value: prefix.length > 0 ? `?.${prefix}` : '?.' });
|
|
85
117
|
return new Proxy(handler, {
|
|
86
118
|
get(_, prop) {
|
|
87
|
-
if (prop === '
|
|
88
|
-
return prefix
|
|
119
|
+
if (prop === 'Path' || prop === PATH_SYMBOL) {
|
|
120
|
+
return serializePath(prefix);
|
|
89
121
|
}
|
|
90
122
|
// Ignore symbol access (Symbol.iterator, Symbol.toPrimitive, etc.)
|
|
91
123
|
if (typeof prop === 'symbol')
|
|
92
124
|
return undefined;
|
|
125
|
+
const reservedToken = getReservedToken(String(prop));
|
|
126
|
+
if (reservedToken === 'Path') {
|
|
127
|
+
return serializePath(prefix);
|
|
128
|
+
}
|
|
129
|
+
if (reservedToken === 'Each') {
|
|
130
|
+
return createPathProxy(appendPathSegment(prefix, '@each'), options);
|
|
131
|
+
}
|
|
132
|
+
if (reservedToken) {
|
|
133
|
+
return createPathProxy(appendCommandSuffix(prefix, reservedToken), options);
|
|
134
|
+
}
|
|
93
135
|
let segment = String(prop);
|
|
94
136
|
// #-prefix: $['#firstName'] → '#[firstName]' (cached element ref)
|
|
95
137
|
if (segment.startsWith('#')) {
|
|
@@ -107,7 +149,7 @@ function createPathProxy(prefix, options) {
|
|
|
107
149
|
}
|
|
108
150
|
}
|
|
109
151
|
}
|
|
110
|
-
const newPath = prefix
|
|
152
|
+
const newPath = appendPathSegment(prefix, segment);
|
|
111
153
|
return createPathProxy(newPath, options);
|
|
112
154
|
},
|
|
113
155
|
apply(_, __, args) {
|
|
@@ -126,7 +168,7 @@ function createPathProxy(prefix, options) {
|
|
|
126
168
|
}
|
|
127
169
|
else
|
|
128
170
|
argStr = String(arg);
|
|
129
|
-
const newPath = prefix
|
|
171
|
+
const newPath = appendPathSegment(prefix, argStr);
|
|
130
172
|
return createPathProxy(newPath, options);
|
|
131
173
|
}
|
|
132
174
|
// No args — method called with no arguments, return self
|
|
@@ -143,21 +185,21 @@ function createPathProxy(prefix, options) {
|
|
|
143
185
|
*
|
|
144
186
|
* @example
|
|
145
187
|
* const $ = paths<Person>();
|
|
146
|
-
* $.lastName.
|
|
147
|
-
* $.address.city.
|
|
188
|
+
* $.lastName.Path // '?.lastName'
|
|
189
|
+
* $.address.city.Path // '?.address?.city'
|
|
148
190
|
*
|
|
149
191
|
* // With aka (reverse alias applied):
|
|
150
192
|
* const $ = paths<MyEl>({ aka: { q: 'querySelector' } });
|
|
151
|
-
* $.querySelector('.user').textContent.
|
|
193
|
+
* $.querySelector('.user').textContent.Path // '?.q?..user?.textContent'
|
|
152
194
|
*
|
|
153
|
-
* // Inside sp template literals, .
|
|
195
|
+
* // Inside sp template literals, .Path is not needed:
|
|
154
196
|
* sp`${$.lastName}, ${$.firstName}` // ['?.lastName', ', ', '?.firstName']
|
|
155
197
|
*/
|
|
156
198
|
export function paths(options) {
|
|
157
199
|
return createPathProxy('', options);
|
|
158
200
|
}
|
|
159
201
|
/**
|
|
160
|
-
* Create an assignment pair: { [lhs.
|
|
202
|
+
* Create an assignment pair: { [lhs.Path]: rhs.Path }.
|
|
161
203
|
* Used to express "set this target to this source value" in a spreadable form.
|
|
162
204
|
*
|
|
163
205
|
* @example
|
|
@@ -276,7 +318,7 @@ export function forEachKeyIn(keys, factory, options) {
|
|
|
276
318
|
* Interleaves static string segments with interpolated values.
|
|
277
319
|
*
|
|
278
320
|
* Path proxy objects are auto-detected and converted to their `?.`-prefixed
|
|
279
|
-
* string representation — no `.
|
|
321
|
+
* string representation — no `.Path` call needed inside sp template literals.
|
|
280
322
|
*
|
|
281
323
|
* Arrays passed as interpolations are preserved as nested arrays (for
|
|
282
324
|
* all-or-nothing optional segments in builtIns.join).
|
|
@@ -319,8 +361,10 @@ export function sp(strings, ...values) {
|
|
|
319
361
|
* e.g., '?.address?.city' → 'city', '?.firstName' → 'firstName'
|
|
320
362
|
*/
|
|
321
363
|
function extractPropName(pathStr) {
|
|
322
|
-
const
|
|
323
|
-
|
|
364
|
+
const withoutCommand = pathStr.replace(/(?: \+=| =!| \?=| Y=| -=| =>)$/, '');
|
|
365
|
+
const parts = withoutCommand.split('?.');
|
|
366
|
+
const last = parts[parts.length - 1];
|
|
367
|
+
return last === '@each' ? 'Each' : last;
|
|
324
368
|
}
|
|
325
369
|
/**
|
|
326
370
|
* Tagged template literal that produces an array of {prop, val} objects + literal strings.
|
package/DX/paths.ts
CHANGED
|
@@ -19,33 +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
|
-
}
|
|
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
|
+
}
|
|
35
65
|
|
|
36
66
|
/**
|
|
37
67
|
* Type that maps an object type to a proxy where every property access
|
|
38
68
|
* returns either a deeper proxy (for object properties) or a terminal
|
|
39
|
-
* with a
|
|
40
|
-
* Enhanced: also callable (for method call syntax)
|
|
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`.
|
|
41
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
|
+
|
|
42
84
|
export type PathProxy<T> = {
|
|
43
85
|
[K in keyof T]-?: T[K] extends ((...args: any[]) => infer R)
|
|
44
|
-
? ((...args: any[]) => PathProxy<NonNullable<R>> &
|
|
86
|
+
? ((...args: any[]) => PathProxy<NonNullable<R>> & PathProxyCore) & PathProxy<NonNullable<R>> & PathProxyCore
|
|
45
87
|
: T[K] extends (object | undefined | null)
|
|
46
|
-
? PathProxy<NonNullable<T[K]>> &
|
|
47
|
-
:
|
|
48
|
-
} &
|
|
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);
|
|
49
91
|
|
|
50
92
|
/**
|
|
51
93
|
* Options for paths proxy creation.
|
|
@@ -60,33 +102,41 @@ export interface PathsOptions {
|
|
|
60
102
|
/**
|
|
61
103
|
* Create a proxy for id-ref paths (#[varName]).
|
|
62
104
|
* After the initial #[varName], further property access chains with ?. from the resolved element.
|
|
63
|
-
* .
|
|
105
|
+
* .Path returns the #[varName] prefix (optionally with further ?. path).
|
|
64
106
|
*/
|
|
65
|
-
function createIdRefProxy(idRef: string, options?: PathsOptions): any {
|
|
66
|
-
function handler() {}
|
|
67
|
-
Object.defineProperty(handler, PATH_SYMBOL, { value: idRef });
|
|
68
|
-
return new Proxy(handler, {
|
|
69
|
-
get(_, prop: string | symbol) {
|
|
70
|
-
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) {
|
|
71
113
|
return idRef;
|
|
72
114
|
}
|
|
73
115
|
if (typeof prop === 'symbol') return undefined;
|
|
74
116
|
|
|
117
|
+
const reservedToken = getReservedToken(prop);
|
|
118
|
+
if (reservedToken === 'Each') {
|
|
119
|
+
return createIdRefProxy(appendPathSegment(idRef, '@each'), options);
|
|
120
|
+
}
|
|
121
|
+
if (reservedToken && reservedToken !== 'Path') {
|
|
122
|
+
return createIdRefProxy(appendCommandSuffix(idRef, reservedToken), options);
|
|
123
|
+
}
|
|
124
|
+
|
|
75
125
|
// Chain further path segments after the id ref
|
|
76
|
-
const chained =
|
|
126
|
+
const chained = appendPathSegment(idRef, String(prop));
|
|
77
127
|
return createIdRefProxy(chained, options);
|
|
78
128
|
},
|
|
79
129
|
apply(_, __, args) {
|
|
80
130
|
if (args.length > 0) {
|
|
81
|
-
const arg = args[0];
|
|
82
|
-
let argStr: string;
|
|
83
|
-
if (arg === true) argStr = 'true';
|
|
84
|
-
else if (arg === false) argStr = 'false';
|
|
85
|
-
else if (isPathProxy(arg)) {
|
|
86
|
-
const fullPath = arg[PATH_SYMBOL] as string;
|
|
87
|
-
argStr = fullPath.startsWith('?.') ? fullPath.substring(2) : fullPath;
|
|
88
|
-
}
|
|
89
|
-
else argStr = String(arg);
|
|
131
|
+
const arg = args[0];
|
|
132
|
+
let argStr: string;
|
|
133
|
+
if (arg === true) argStr = 'true';
|
|
134
|
+
else if (arg === false) argStr = 'false';
|
|
135
|
+
else if (isPathProxy(arg)) {
|
|
136
|
+
const fullPath = arg[PATH_SYMBOL] as string;
|
|
137
|
+
argStr = fullPath.startsWith('?.') ? fullPath.substring(2) : fullPath;
|
|
138
|
+
}
|
|
139
|
+
else argStr = String(arg);
|
|
90
140
|
|
|
91
141
|
const chained = `${idRef}?.${argStr}`;
|
|
92
142
|
return createIdRefProxy(chained, options);
|
|
@@ -103,21 +153,32 @@ function createIdRefProxy(idRef: string, options?: PathsOptions): any {
|
|
|
103
153
|
* When `aka` is provided, property names that match an alias *value* are output
|
|
104
154
|
* using the alias *key* instead (reverse alias).
|
|
105
155
|
*/
|
|
106
|
-
function createPathProxy(prefix: string, options?: PathsOptions): any {
|
|
107
|
-
const aliasMap = options?.aka;
|
|
108
|
-
|
|
109
|
-
// Use a function as the target to enable the apply trap
|
|
110
|
-
function handler() {}
|
|
111
|
-
Object.defineProperty(handler, PATH_SYMBOL, { value: prefix.length > 0 ? `?.${prefix}` : '?.' });
|
|
112
|
-
|
|
113
|
-
return new Proxy(handler, {
|
|
114
|
-
get(_, prop: string | symbol) {
|
|
115
|
-
if (prop === '
|
|
116
|
-
return prefix
|
|
117
|
-
}
|
|
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) {
|
|
166
|
+
return serializePath(prefix);
|
|
167
|
+
}
|
|
118
168
|
// Ignore symbol access (Symbol.iterator, Symbol.toPrimitive, etc.)
|
|
119
169
|
if (typeof prop === 'symbol') return undefined;
|
|
120
170
|
|
|
171
|
+
const reservedToken = getReservedToken(String(prop));
|
|
172
|
+
if (reservedToken === 'Path') {
|
|
173
|
+
return serializePath(prefix);
|
|
174
|
+
}
|
|
175
|
+
if (reservedToken === 'Each') {
|
|
176
|
+
return createPathProxy(appendPathSegment(prefix, '@each'), options);
|
|
177
|
+
}
|
|
178
|
+
if (reservedToken) {
|
|
179
|
+
return createPathProxy(appendCommandSuffix(prefix, reservedToken), options);
|
|
180
|
+
}
|
|
181
|
+
|
|
121
182
|
let segment = String(prop);
|
|
122
183
|
|
|
123
184
|
// #-prefix: $['#firstName'] → '#[firstName]' (cached element ref)
|
|
@@ -135,24 +196,24 @@ function createPathProxy(prefix: string, options?: PathsOptions): any {
|
|
|
135
196
|
}
|
|
136
197
|
}
|
|
137
198
|
|
|
138
|
-
const newPath = prefix
|
|
199
|
+
const newPath = appendPathSegment(prefix, segment);
|
|
139
200
|
return createPathProxy(newPath, options);
|
|
140
201
|
},
|
|
141
202
|
apply(_, __, args) {
|
|
142
203
|
// Method call syntax: $.querySelector('.username') → extends path with the argument
|
|
143
204
|
if (args.length > 0) {
|
|
144
|
-
const arg = args[0];
|
|
145
|
-
let argStr: string;
|
|
146
|
-
if (arg === true) argStr = 'true';
|
|
147
|
-
else if (arg === false) argStr = 'false';
|
|
148
|
-
else if (isPathProxy(arg)) {
|
|
149
|
-
// Proxy arg — extract path without '?.' prefix
|
|
150
|
-
const fullPath = arg[PATH_SYMBOL] as string;
|
|
151
|
-
argStr = fullPath.startsWith('?.') ? fullPath.substring(2) : fullPath;
|
|
152
|
-
}
|
|
153
|
-
else argStr = String(arg);
|
|
154
|
-
|
|
155
|
-
const newPath = prefix
|
|
205
|
+
const arg = args[0];
|
|
206
|
+
let argStr: string;
|
|
207
|
+
if (arg === true) argStr = 'true';
|
|
208
|
+
else if (arg === false) argStr = 'false';
|
|
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);
|
|
156
217
|
return createPathProxy(newPath, options);
|
|
157
218
|
}
|
|
158
219
|
// No args — method called with no arguments, return self
|
|
@@ -170,14 +231,14 @@ function createPathProxy(prefix: string, options?: PathsOptions): any {
|
|
|
170
231
|
*
|
|
171
232
|
* @example
|
|
172
233
|
* const $ = paths<Person>();
|
|
173
|
-
* $.lastName.
|
|
174
|
-
* $.address.city.
|
|
234
|
+
* $.lastName.Path // '?.lastName'
|
|
235
|
+
* $.address.city.Path // '?.address?.city'
|
|
175
236
|
*
|
|
176
237
|
* // With aka (reverse alias applied):
|
|
177
238
|
* const $ = paths<MyEl>({ aka: { q: 'querySelector' } });
|
|
178
|
-
* $.querySelector('.user').textContent.
|
|
239
|
+
* $.querySelector('.user').textContent.Path // '?.q?..user?.textContent'
|
|
179
240
|
*
|
|
180
|
-
* // Inside sp template literals, .
|
|
241
|
+
* // Inside sp template literals, .Path is not needed:
|
|
181
242
|
* sp`${$.lastName}, ${$.firstName}` // ['?.lastName', ', ', '?.firstName']
|
|
182
243
|
*/
|
|
183
244
|
export function paths<T>(options?: PathsOptions): PathProxy<T> {
|
|
@@ -185,7 +246,7 @@ export function paths<T>(options?: PathsOptions): PathProxy<T> {
|
|
|
185
246
|
}
|
|
186
247
|
|
|
187
248
|
/**
|
|
188
|
-
* Create an assignment pair: { [lhs.
|
|
249
|
+
* Create an assignment pair: { [lhs.Path]: rhs.Path }.
|
|
189
250
|
* Used to express "set this target to this source value" in a spreadable form.
|
|
190
251
|
*
|
|
191
252
|
* @example
|
|
@@ -199,18 +260,18 @@ export function paths<T>(options?: PathsOptions): PathProxy<T> {
|
|
|
199
260
|
* count: 1
|
|
200
261
|
* }
|
|
201
262
|
*/
|
|
202
|
-
export function set(lhs: any): { to: (rhs: any) => Record<string, any> } {
|
|
203
|
-
const lhsStr = isPathProxy(lhs)
|
|
204
|
-
? lhs[PATH_SYMBOL]
|
|
205
|
-
: String(lhs);
|
|
206
|
-
return {
|
|
207
|
-
to(rhs: any): Record<string, any> {
|
|
208
|
-
const rhsStr = isPathProxy(rhs)
|
|
209
|
-
? rhs[PATH_SYMBOL]
|
|
210
|
-
: rhs;
|
|
211
|
-
return { [lhsStr]: rhsStr };
|
|
212
|
-
}
|
|
213
|
-
};
|
|
263
|
+
export function set(lhs: any): { to: (rhs: any) => Record<string, any> } {
|
|
264
|
+
const lhsStr = isPathProxy(lhs)
|
|
265
|
+
? lhs[PATH_SYMBOL]
|
|
266
|
+
: String(lhs);
|
|
267
|
+
return {
|
|
268
|
+
to(rhs: any): Record<string, any> {
|
|
269
|
+
const rhsStr = isPathProxy(rhs)
|
|
270
|
+
? rhs[PATH_SYMBOL]
|
|
271
|
+
: rhs;
|
|
272
|
+
return { [lhsStr]: rhsStr };
|
|
273
|
+
}
|
|
274
|
+
};
|
|
214
275
|
}
|
|
215
276
|
|
|
216
277
|
/**
|
|
@@ -231,19 +292,19 @@ export function set(lhs: any): { to: (rhs: any) => Record<string, any> } {
|
|
|
231
292
|
* });
|
|
232
293
|
* // { assign: { incrementButton: '?.clone?.q?..increment', ... } }
|
|
233
294
|
*/
|
|
234
|
-
export function smoothOver(value: any): any {
|
|
235
|
-
if (isPathProxy(value)) {
|
|
236
|
-
return value[PATH_SYMBOL];
|
|
237
|
-
}
|
|
295
|
+
export function smoothOver(value: any): any {
|
|
296
|
+
if (isPathProxy(value)) {
|
|
297
|
+
return value[PATH_SYMBOL];
|
|
298
|
+
}
|
|
238
299
|
if (Array.isArray(value)) {
|
|
239
300
|
return value.map(smoothOver);
|
|
240
301
|
}
|
|
241
302
|
if (value && typeof value === 'object') {
|
|
242
303
|
const proto = Object.getPrototypeOf(value);
|
|
243
304
|
if (proto === Object.prototype || proto === null) {
|
|
244
|
-
const result: Record<string, any> = {};
|
|
245
|
-
for (const [k, v] of Object.entries(value)) {
|
|
246
|
-
result[k] = smoothOver(v);
|
|
305
|
+
const result: Record<string, any> = {};
|
|
306
|
+
for (const [k, v] of Object.entries(value)) {
|
|
307
|
+
result[k] = smoothOver(v);
|
|
247
308
|
}
|
|
248
309
|
return result;
|
|
249
310
|
}
|
|
@@ -312,7 +373,7 @@ export function forEachKeyIn<T>(
|
|
|
312
373
|
* Interleaves static string segments with interpolated values.
|
|
313
374
|
*
|
|
314
375
|
* Path proxy objects are auto-detected and converted to their `?.`-prefixed
|
|
315
|
-
* string representation — no `.
|
|
376
|
+
* string representation — no `.Path` call needed inside sp template literals.
|
|
316
377
|
*
|
|
317
378
|
* Arrays passed as interpolations are preserved as nested arrays (for
|
|
318
379
|
* all-or-nothing optional segments in builtIns.join).
|
|
@@ -328,23 +389,23 @@ export function forEachKeyIn<T>(
|
|
|
328
389
|
* sp`${$.lastName}${[', ', $.middleName]}, ${$.firstName}`
|
|
329
390
|
* // ['?.lastName', [', ', '?.middleName'], ', ', '?.firstName']
|
|
330
391
|
*/
|
|
331
|
-
export function sp(strings: TemplateStringsArray, ...values: any[]): any[] {
|
|
392
|
+
export function sp(strings: TemplateStringsArray, ...values: any[]): any[] {
|
|
332
393
|
const result: any[] = [];
|
|
333
394
|
for (let i = 0; i < strings.length; i++) {
|
|
334
395
|
if (strings[i]) result.push(strings[i]);
|
|
335
396
|
if (i < values.length) {
|
|
336
|
-
const v = values[i];
|
|
337
|
-
if (isPathProxy(v)) {
|
|
338
|
-
// Auto-extract path from proxy object
|
|
339
|
-
result.push(v[PATH_SYMBOL]);
|
|
340
|
-
} else if (Array.isArray(v)) {
|
|
341
|
-
// Nested array — recursively extract paths from proxy elements
|
|
342
|
-
result.push(v.map(el =>
|
|
343
|
-
isPathProxy(el) ? el[PATH_SYMBOL] : el
|
|
344
|
-
));
|
|
345
|
-
} else {
|
|
346
|
-
result.push(v);
|
|
347
|
-
}
|
|
397
|
+
const v = values[i];
|
|
398
|
+
if (isPathProxy(v)) {
|
|
399
|
+
// Auto-extract path from proxy object
|
|
400
|
+
result.push(v[PATH_SYMBOL]);
|
|
401
|
+
} else if (Array.isArray(v)) {
|
|
402
|
+
// Nested array — recursively extract paths from proxy elements
|
|
403
|
+
result.push(v.map(el =>
|
|
404
|
+
isPathProxy(el) ? el[PATH_SYMBOL] : el
|
|
405
|
+
));
|
|
406
|
+
} else {
|
|
407
|
+
result.push(v);
|
|
408
|
+
}
|
|
348
409
|
}
|
|
349
410
|
}
|
|
350
411
|
return result;
|
|
@@ -355,8 +416,10 @@ export function sp(strings: TemplateStringsArray, ...values: any[]): any[] {
|
|
|
355
416
|
* e.g., '?.address?.city' → 'city', '?.firstName' → 'firstName'
|
|
356
417
|
*/
|
|
357
418
|
function extractPropName(pathStr: string): string {
|
|
358
|
-
const
|
|
359
|
-
|
|
419
|
+
const withoutCommand = pathStr.replace(/(?: \+=| =!| \?=| Y=| -=| =>)$/, '');
|
|
420
|
+
const parts = withoutCommand.split('?.');
|
|
421
|
+
const last = parts[parts.length - 1];
|
|
422
|
+
return last === '@each' ? 'Each' : last;
|
|
360
423
|
}
|
|
361
424
|
|
|
362
425
|
/**
|
|
@@ -389,27 +452,27 @@ export function md(strings: TemplateStringsArray, ...values: any[]): any[] {
|
|
|
389
452
|
for (let i = 0; i < strings.length; i++) {
|
|
390
453
|
if (strings[i]) result.push(strings[i]);
|
|
391
454
|
if (i < values.length) {
|
|
392
|
-
const v = values[i];
|
|
393
|
-
if (isPathProxy(v)) {
|
|
394
|
-
// Proxy object → {prop, val}
|
|
395
|
-
const pathStr = v[PATH_SYMBOL] as string;
|
|
396
|
-
result.push({ prop: extractPropName(pathStr), val: pathStr });
|
|
397
|
-
} else if (Array.isArray(v)) {
|
|
398
|
-
// Nested array — recursively convert proxy elements to {prop, val}
|
|
399
|
-
result.push(v.map(el => {
|
|
400
|
-
if (isPathProxy(el)) {
|
|
401
|
-
const pathStr = el[PATH_SYMBOL] as string;
|
|
402
|
-
return { prop: extractPropName(pathStr), val: pathStr };
|
|
403
|
-
}
|
|
404
|
-
return el;
|
|
405
|
-
}));
|
|
406
|
-
} else if (v && typeof v === 'object' && 'prop' in v) {
|
|
407
|
-
// Developer override object — extract val from proxy if present
|
|
408
|
-
const processed = { ...v };
|
|
409
|
-
if (isPathProxy(processed.val)) {
|
|
410
|
-
processed.val = processed.val[PATH_SYMBOL];
|
|
411
|
-
}
|
|
412
|
-
result.push(processed);
|
|
455
|
+
const v = values[i];
|
|
456
|
+
if (isPathProxy(v)) {
|
|
457
|
+
// Proxy object → {prop, val}
|
|
458
|
+
const pathStr = v[PATH_SYMBOL] as string;
|
|
459
|
+
result.push({ prop: extractPropName(pathStr), val: pathStr });
|
|
460
|
+
} else if (Array.isArray(v)) {
|
|
461
|
+
// Nested array — recursively convert proxy elements to {prop, val}
|
|
462
|
+
result.push(v.map(el => {
|
|
463
|
+
if (isPathProxy(el)) {
|
|
464
|
+
const pathStr = el[PATH_SYMBOL] as string;
|
|
465
|
+
return { prop: extractPropName(pathStr), val: pathStr };
|
|
466
|
+
}
|
|
467
|
+
return el;
|
|
468
|
+
}));
|
|
469
|
+
} else if (v && typeof v === 'object' && 'prop' in v) {
|
|
470
|
+
// Developer override object — extract val from proxy if present
|
|
471
|
+
const processed = { ...v };
|
|
472
|
+
if (isPathProxy(processed.val)) {
|
|
473
|
+
processed.val = processed.val[PATH_SYMBOL];
|
|
474
|
+
}
|
|
475
|
+
result.push(processed);
|
|
413
476
|
} else {
|
|
414
477
|
result.push(v);
|
|
415
478
|
}
|
package/README.md
CHANGED
|
@@ -4400,9 +4400,10 @@ export default {
|
|
|
4400
4400
|
**How `paths` works:**
|
|
4401
4401
|
|
|
4402
4402
|
- `paths<T>()` creates a deeply-proxied object typed as `T`
|
|
4403
|
-
- Every property access returns a deeper proxy (e.g., `$.address.city`)
|
|
4404
|
-
- `.
|
|
4405
|
-
-
|
|
4403
|
+
- Every property access returns a deeper proxy (e.g., `$.address.city`)
|
|
4404
|
+
- Capitalized reserved tokens like `.Each` and `.EqNot` normalize into command syntax
|
|
4405
|
+
- `.Path` extracts the `?.`-prefixed string: `$.address.city.Path` → `'?.address?.city'`
|
|
4406
|
+
- Inside `sp` template literals, `.Path` is not needed — proxy objects are auto-detected
|
|
4406
4407
|
|
|
4407
4408
|
**How `sp` works:**
|
|
4408
4409
|
|
|
@@ -4421,19 +4422,19 @@ const value = sp`${$.lastName}${[', ', $.middleName]}, ${$.firstName}`;
|
|
|
4421
4422
|
// ['?.lastName', [', ', '?.middleName'], ', ', '?.firstName']
|
|
4422
4423
|
```
|
|
4423
4424
|
|
|
4424
|
-
**Using `.
|
|
4425
|
+
**Using `.Path` outside of `sp`:**
|
|
4425
4426
|
|
|
4426
4427
|
When you need the path string in a non-`sp` context (object keys, plain arrays, other expressions):
|
|
4427
4428
|
|
|
4428
4429
|
```TypeScript
|
|
4429
4430
|
const $ = paths<Person>();
|
|
4430
4431
|
|
|
4431
|
-
$.lastName.
|
|
4432
|
-
$.address.city.
|
|
4432
|
+
$.lastName.Path // '?.lastName'
|
|
4433
|
+
$.address.city.Path // '?.address?.city'
|
|
4433
4434
|
|
|
4434
4435
|
// As an object key:
|
|
4435
4436
|
const pattern = {
|
|
4436
|
-
[$.textContent.
|
|
4437
|
+
[$.textContent.Path]: '?.firstName' // '?.textContent': '?.firstName'
|
|
4437
4438
|
};
|
|
4438
4439
|
```
|
|
4439
4440
|
|
|
@@ -4473,7 +4474,7 @@ md`${$.firstName} ${{ prop: 'birthDate', val: $.birthDT, format: 'long' }}`
|
|
|
4473
4474
|
| `sp` | `'?.firstName'` (path string) | `builtIns.join` |
|
|
4474
4475
|
| `md` | `{ prop: 'firstName', val: '?.firstName' }` | `builtIns.microDataJoin` |
|
|
4475
4476
|
|
|
4476
|
-
Both auto-detect path proxies (no `.
|
|
4477
|
+
Both auto-detect path proxies (no `.Path` needed inside template literals) and preserve nested arrays for optional segments.
|
|
4477
4478
|
|
|
4478
4479
|
## Cached Element Resolution with `#[x]` and `pin`
|
|
4479
4480
|
|