assign-gingerly 0.0.80 → 0.0.81
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/paths.js +52 -8
- package/DX/paths.ts +178 -114
- package/README.md +9 -8
- package/inferencer/types/NewCustomElement.md +20 -440
- package/inferencer/types/NewHTMLFirstCustomElement.md +431 -0
- package/inferencer/types/NewJSFirstCustomElement.md +278 -0
- package/package.json +1 -1
package/DX/paths.js
CHANGED
|
@@ -30,6 +30,31 @@ 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' || 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.
|
|
@@ -40,13 +65,20 @@ function createIdRefProxy(idRef, options) {
|
|
|
40
65
|
Object.defineProperty(handler, PATH_SYMBOL, { value: idRef });
|
|
41
66
|
return new Proxy(handler, {
|
|
42
67
|
get(_, prop) {
|
|
43
|
-
if (prop === 'path' || prop === PATH_SYMBOL) {
|
|
68
|
+
if (prop === 'path' || 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' && 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 === 'path' || prop === PATH_SYMBOL) {
|
|
88
|
-
return prefix
|
|
119
|
+
if (prop === 'path' || 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' || 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
|
|
@@ -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
|
@@ -32,20 +32,63 @@ const PATH_SYMBOL = Symbol('assign-gingerly-path');
|
|
|
32
32
|
function isPathProxy(value: unknown): value is { [PATH_SYMBOL]: string } {
|
|
33
33
|
return !!value && (typeof value === 'object' || typeof value === 'function') && PATH_SYMBOL in value;
|
|
34
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' | 'path' | undefined {
|
|
61
|
+
if (prop === 'Path' || prop === 'path') return prop;
|
|
62
|
+
if (prop in COMMAND_TOKEN_SUFFIXES) return prop as CommandToken;
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
35
65
|
|
|
36
66
|
/**
|
|
37
|
-
* Type that maps an object type to a proxy where every property access
|
|
38
|
-
* returns either a deeper proxy (for object properties) or a terminal
|
|
39
|
-
* with a
|
|
40
|
-
* Enhanced: also callable (for method call syntax)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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 path: string;
|
|
76
|
+
readonly Each: PathProxy<any>;
|
|
77
|
+
readonly EqNot: PathProxy<any>;
|
|
78
|
+
readonly PlusEq: PathProxy<any>;
|
|
79
|
+
readonly QMEq: PathProxy<any>;
|
|
80
|
+
readonly YEq: PathProxy<any>;
|
|
81
|
+
readonly MinusEq: PathProxy<any>;
|
|
82
|
+
readonly Arrow: PathProxy<any>;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export type PathProxy<T> = {
|
|
86
|
+
[K in keyof T]-?: T[K] extends ((...args: any[]) => infer R)
|
|
87
|
+
? ((...args: any[]) => PathProxy<NonNullable<R>> & PathProxyCore) & PathProxy<NonNullable<R>> & PathProxyCore
|
|
88
|
+
: T[K] extends (object | undefined | null)
|
|
89
|
+
? PathProxy<NonNullable<T[K]>> & PathProxyCore & ((...args: any[]) => PathProxy<any> & PathProxyCore)
|
|
90
|
+
: PathProxyCore & ((...args: any[]) => PathProxy<any> & PathProxyCore);
|
|
91
|
+
} & PathProxyCore & ((...args: any[]) => PathProxy<any> & PathProxyCore);
|
|
49
92
|
|
|
50
93
|
/**
|
|
51
94
|
* Options for paths proxy creation.
|
|
@@ -67,26 +110,34 @@ function createIdRefProxy(idRef: string, options?: PathsOptions): any {
|
|
|
67
110
|
Object.defineProperty(handler, PATH_SYMBOL, { value: idRef });
|
|
68
111
|
return new Proxy(handler, {
|
|
69
112
|
get(_, prop: string | symbol) {
|
|
70
|
-
if (prop === 'path' || prop === PATH_SYMBOL) {
|
|
113
|
+
if (prop === 'path' || prop === 'Path' || prop === PATH_SYMBOL) {
|
|
71
114
|
return idRef;
|
|
72
115
|
}
|
|
73
|
-
if (typeof prop === 'symbol') return undefined;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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' && reservedToken !== 'path') {
|
|
123
|
+
return createIdRefProxy(appendCommandSuffix(idRef, reservedToken), options);
|
|
124
|
+
}
|
|
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) {
|
|
81
132
|
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);
|
|
133
|
+
let argStr: string;
|
|
134
|
+
if (arg === true) argStr = 'true';
|
|
135
|
+
else if (arg === false) argStr = 'false';
|
|
136
|
+
else if (isPathProxy(arg)) {
|
|
137
|
+
const fullPath = arg[PATH_SYMBOL] as string;
|
|
138
|
+
argStr = fullPath.startsWith('?.') ? fullPath.substring(2) : fullPath;
|
|
139
|
+
}
|
|
140
|
+
else argStr = String(arg);
|
|
90
141
|
|
|
91
142
|
const chained = `${idRef}?.${argStr}`;
|
|
92
143
|
return createIdRefProxy(chained, options);
|
|
@@ -112,52 +163,63 @@ function createPathProxy(prefix: string, options?: PathsOptions): any {
|
|
|
112
163
|
|
|
113
164
|
return new Proxy(handler, {
|
|
114
165
|
get(_, prop: string | symbol) {
|
|
115
|
-
if (prop === 'path' || prop === PATH_SYMBOL) {
|
|
116
|
-
return prefix
|
|
117
|
-
}
|
|
118
|
-
// Ignore symbol access (Symbol.iterator, Symbol.toPrimitive, etc.)
|
|
119
|
-
if (typeof prop === 'symbol') return undefined;
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
166
|
+
if (prop === 'path' || prop === 'Path' || prop === PATH_SYMBOL) {
|
|
167
|
+
return serializePath(prefix);
|
|
168
|
+
}
|
|
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' || reservedToken === 'path') {
|
|
174
|
+
return serializePath(prefix);
|
|
175
|
+
}
|
|
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}]`;
|
|
127
189
|
// Return a proxy that starts from this id ref (can chain further with ?.)
|
|
128
190
|
return createIdRefProxy(idRef, options);
|
|
129
191
|
}
|
|
130
192
|
|
|
131
193
|
// Apply reverse alias: if prop matches an alias value, use the alias key
|
|
132
|
-
if (aliasMap) {
|
|
133
|
-
for (const [alias, target] of Object.entries(aliasMap)) {
|
|
134
|
-
if (target === segment) { segment = alias; break; }
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const newPath = prefix
|
|
139
|
-
return createPathProxy(newPath, options);
|
|
140
|
-
},
|
|
141
|
-
apply(_, __, args) {
|
|
142
|
-
// Method call syntax: $.querySelector('.username') → extends path with the argument
|
|
143
|
-
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';
|
|
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) {
|
|
206
|
+
const arg = args[0];
|
|
207
|
+
let argStr: string;
|
|
208
|
+
if (arg === true) argStr = 'true';
|
|
209
|
+
else if (arg === false) argStr = 'false';
|
|
148
210
|
else if (isPathProxy(arg)) {
|
|
149
211
|
// Proxy arg — extract path without '?.' prefix
|
|
150
212
|
const fullPath = arg[PATH_SYMBOL] as string;
|
|
151
213
|
argStr = fullPath.startsWith('?.') ? fullPath.substring(2) : fullPath;
|
|
152
214
|
}
|
|
153
215
|
else argStr = String(arg);
|
|
154
|
-
|
|
155
|
-
const newPath = prefix
|
|
156
|
-
return createPathProxy(newPath, options);
|
|
157
|
-
}
|
|
158
|
-
// No args — method called with no arguments, return self
|
|
159
|
-
return createPathProxy(prefix, options);
|
|
160
|
-
}
|
|
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
|
+
}
|
|
161
223
|
});
|
|
162
224
|
}
|
|
163
225
|
|
|
@@ -203,14 +265,14 @@ export function set(lhs: any): { to: (rhs: any) => Record<string, any> } {
|
|
|
203
265
|
const lhsStr = isPathProxy(lhs)
|
|
204
266
|
? lhs[PATH_SYMBOL]
|
|
205
267
|
: 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
|
-
};
|
|
268
|
+
return {
|
|
269
|
+
to(rhs: any): Record<string, any> {
|
|
270
|
+
const rhsStr = isPathProxy(rhs)
|
|
271
|
+
? rhs[PATH_SYMBOL]
|
|
272
|
+
: rhs;
|
|
273
|
+
return { [lhsStr]: rhsStr };
|
|
274
|
+
}
|
|
275
|
+
};
|
|
214
276
|
}
|
|
215
277
|
|
|
216
278
|
/**
|
|
@@ -234,16 +296,16 @@ export function set(lhs: any): { to: (rhs: any) => Record<string, any> } {
|
|
|
234
296
|
export function smoothOver(value: any): any {
|
|
235
297
|
if (isPathProxy(value)) {
|
|
236
298
|
return value[PATH_SYMBOL];
|
|
237
|
-
}
|
|
299
|
+
}
|
|
238
300
|
if (Array.isArray(value)) {
|
|
239
301
|
return value.map(smoothOver);
|
|
240
302
|
}
|
|
241
303
|
if (value && typeof value === 'object') {
|
|
242
304
|
const proto = Object.getPrototypeOf(value);
|
|
243
305
|
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);
|
|
306
|
+
const result: Record<string, any> = {};
|
|
307
|
+
for (const [k, v] of Object.entries(value)) {
|
|
308
|
+
result[k] = smoothOver(v);
|
|
247
309
|
}
|
|
248
310
|
return result;
|
|
249
311
|
}
|
|
@@ -328,23 +390,23 @@ export function forEachKeyIn<T>(
|
|
|
328
390
|
* sp`${$.lastName}${[', ', $.middleName]}, ${$.firstName}`
|
|
329
391
|
* // ['?.lastName', [', ', '?.middleName'], ', ', '?.firstName']
|
|
330
392
|
*/
|
|
331
|
-
export function sp(strings: TemplateStringsArray, ...values: any[]): any[] {
|
|
393
|
+
export function sp(strings: TemplateStringsArray, ...values: any[]): any[] {
|
|
332
394
|
const result: any[] = [];
|
|
333
395
|
for (let i = 0; i < strings.length; i++) {
|
|
334
396
|
if (strings[i]) result.push(strings[i]);
|
|
335
397
|
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
|
-
}
|
|
398
|
+
const v = values[i];
|
|
399
|
+
if (isPathProxy(v)) {
|
|
400
|
+
// Auto-extract path from proxy object
|
|
401
|
+
result.push(v[PATH_SYMBOL]);
|
|
402
|
+
} else if (Array.isArray(v)) {
|
|
403
|
+
// Nested array — recursively extract paths from proxy elements
|
|
404
|
+
result.push(v.map(el =>
|
|
405
|
+
isPathProxy(el) ? el[PATH_SYMBOL] : el
|
|
406
|
+
));
|
|
407
|
+
} else {
|
|
408
|
+
result.push(v);
|
|
409
|
+
}
|
|
348
410
|
}
|
|
349
411
|
}
|
|
350
412
|
return result;
|
|
@@ -354,10 +416,12 @@ export function sp(strings: TemplateStringsArray, ...values: any[]): any[] {
|
|
|
354
416
|
* Extract the last segment from a `?.`-prefixed path string.
|
|
355
417
|
* e.g., '?.address?.city' → 'city', '?.firstName' → 'firstName'
|
|
356
418
|
*/
|
|
357
|
-
function extractPropName(pathStr: string): string {
|
|
358
|
-
const
|
|
359
|
-
|
|
360
|
-
|
|
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
|
+
}
|
|
361
425
|
|
|
362
426
|
/**
|
|
363
427
|
* Tagged template literal that produces an array of {prop, val} objects + literal strings.
|
|
@@ -389,27 +453,27 @@ export function md(strings: TemplateStringsArray, ...values: any[]): any[] {
|
|
|
389
453
|
for (let i = 0; i < strings.length; i++) {
|
|
390
454
|
if (strings[i]) result.push(strings[i]);
|
|
391
455
|
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);
|
|
456
|
+
const v = values[i];
|
|
457
|
+
if (isPathProxy(v)) {
|
|
458
|
+
// Proxy object → {prop, val}
|
|
459
|
+
const pathStr = v[PATH_SYMBOL] as string;
|
|
460
|
+
result.push({ prop: extractPropName(pathStr), val: pathStr });
|
|
461
|
+
} else if (Array.isArray(v)) {
|
|
462
|
+
// Nested array — recursively convert proxy elements to {prop, val}
|
|
463
|
+
result.push(v.map(el => {
|
|
464
|
+
if (isPathProxy(el)) {
|
|
465
|
+
const pathStr = el[PATH_SYMBOL] as string;
|
|
466
|
+
return { prop: extractPropName(pathStr), val: pathStr };
|
|
467
|
+
}
|
|
468
|
+
return el;
|
|
469
|
+
}));
|
|
470
|
+
} else if (v && typeof v === 'object' && 'prop' in v) {
|
|
471
|
+
// Developer override object — extract val from proxy if present
|
|
472
|
+
const processed = { ...v };
|
|
473
|
+
if (isPathProxy(processed.val)) {
|
|
474
|
+
processed.val = processed.val[PATH_SYMBOL];
|
|
475
|
+
}
|
|
476
|
+
result.push(processed);
|
|
413
477
|
} else {
|
|
414
478
|
result.push(v);
|
|
415
479
|
}
|
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
|
|