@microsoft/fast-html 1.0.0-alpha.19 → 1.0.0-alpha.20
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 +3 -19
- package/dist/dts/components/observer-map.d.ts +3 -120
- package/dist/dts/components/schema.d.ts +23 -15
- package/dist/dts/components/template.d.ts +1 -5
- package/dist/dts/components/utilities.d.ts +33 -69
- package/dist/esm/components/observer-map.js +10 -512
- package/dist/esm/components/observer-map.spec.js +5 -599
- package/dist/esm/components/schema.js +39 -20
- package/dist/esm/components/schema.spec.js +9 -0
- package/dist/esm/components/template.js +31 -50
- package/dist/esm/components/utilities.js +202 -179
- package/dist/esm/components/utilities.spec.js +6 -29
- package/dist/esm/fixtures/repeat/main.js +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/fast-html.api.json +13 -126
- package/dist/fast-html.d.ts +118 -150
- package/dist/fast-html.untrimmed.d.ts +118 -150
- package/package.json +2 -3
- package/dist/dts/fixtures/partial/main.d.ts +0 -1
- package/dist/dts/fixtures/partial/partial.spec.d.ts +0 -1
- package/dist/esm/fixtures/partial/main.js +0 -32
- package/dist/esm/fixtures/partial/partial.spec.js +0 -14
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Observable } from "@microsoft/fast-element/observable.js";
|
|
2
|
+
import { defsPropertyName, refPropertyName, } from "./schema.js";
|
|
2
3
|
const openClientSideBinding = "{";
|
|
3
4
|
const closeClientSideBinding = "}";
|
|
4
5
|
const openContentBinding = "{{";
|
|
@@ -140,6 +141,11 @@ function getAttributeDirectiveDataBindingConfig(innerHTML, config) {
|
|
|
140
141
|
function getContentDataBindingConfig(config) {
|
|
141
142
|
return Object.assign(Object.assign({}, config), { subtype: "content" });
|
|
142
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* Finds the next data binding in innerHTML and determines its type and indices
|
|
146
|
+
* @param innerHTML - The innerHTML string to search for data bindings
|
|
147
|
+
* @returns NextDataBindingBehaviorConfig containing the binding type and start indices
|
|
148
|
+
*/
|
|
143
149
|
function getIndexAndBindingTypeOfNextDataBindingBehavior(innerHTML) {
|
|
144
150
|
// {{{}}} binding
|
|
145
151
|
const openingUnescapedStartIndex = innerHTML.indexOf(openUnescapedBinding);
|
|
@@ -214,36 +220,6 @@ export function getNextBehavior(innerHTML) {
|
|
|
214
220
|
}
|
|
215
221
|
return getNextDataBindingBehavior(innerHTML);
|
|
216
222
|
}
|
|
217
|
-
/**
|
|
218
|
-
* Gets all the partials with their IDs
|
|
219
|
-
* @param innerHTML - The innerHTML string to evaluate
|
|
220
|
-
* @param offset - The index offset from the innerHTML
|
|
221
|
-
* @param partials - The partials found
|
|
222
|
-
* @returns {[key: string]: PartialTemplateConfig}
|
|
223
|
-
*/
|
|
224
|
-
export function getAllPartials(innerHTML, offset = 0, partials = {}) {
|
|
225
|
-
const openingTag = `${openTagStart}partial`;
|
|
226
|
-
const openingTagStartIndex = innerHTML.indexOf(openingTag);
|
|
227
|
-
if (openingTagStartIndex >= 0) {
|
|
228
|
-
const openingTagStartSlice = innerHTML.slice(openingTagStartIndex);
|
|
229
|
-
const closingTag = `${closeTagStart}partial${tagEnd}`;
|
|
230
|
-
const closingTagLength = closingTag.length;
|
|
231
|
-
const matchingCloseTagIndex = getIndexOfNextMatchingTag(openingTagStartSlice, openingTag, closingTag, openingTagStartIndex) + closingTagLength;
|
|
232
|
-
const startId = openingTagStartIndex + ' id="'.length + openingTag.length;
|
|
233
|
-
const endId = innerHTML.slice(startId).indexOf('"') + startId;
|
|
234
|
-
const id = innerHTML.slice(startId, endId);
|
|
235
|
-
const openingTagEndIndex = openingTagStartSlice.indexOf(tagEnd) + 1 + openingTagStartIndex;
|
|
236
|
-
const closingTagStartIndex = matchingCloseTagIndex - closingTagLength;
|
|
237
|
-
partials[id] = {
|
|
238
|
-
innerHTML: innerHTML.slice(openingTagEndIndex, closingTagStartIndex),
|
|
239
|
-
startIndex: openingTagEndIndex + offset,
|
|
240
|
-
endIndex: closingTagStartIndex + offset,
|
|
241
|
-
};
|
|
242
|
-
offset += matchingCloseTagIndex;
|
|
243
|
-
return getAllPartials(innerHTML.slice(matchingCloseTagIndex), offset, partials);
|
|
244
|
-
}
|
|
245
|
-
return partials;
|
|
246
|
-
}
|
|
247
223
|
/**
|
|
248
224
|
* Create a function to resolve a value from an object using a path with dot syntax.
|
|
249
225
|
* e.g. "foo.bar"
|
|
@@ -251,30 +227,38 @@ export function getAllPartials(innerHTML, offset = 0, partials = {}) {
|
|
|
251
227
|
* @param self - Where the first item in the path path refers to the item itself (used by repeat).
|
|
252
228
|
* @returns A function to access the value from a given path.
|
|
253
229
|
*/
|
|
254
|
-
export function pathResolver(path,
|
|
255
|
-
|
|
256
|
-
path.split("
|
|
257
|
-
|
|
258
|
-
|
|
230
|
+
export function pathResolver(path, contextPath, level, rootSchema) {
|
|
231
|
+
var _a, _b;
|
|
232
|
+
let splitPath = path.split(".");
|
|
233
|
+
let levelCount = level;
|
|
234
|
+
let self = splitPath[0] === contextPath;
|
|
235
|
+
const parentContexts = [];
|
|
236
|
+
if (level > 0 &&
|
|
237
|
+
((_b = (_a = rootSchema === null || rootSchema === void 0 ? void 0 : rootSchema[defsPropertyName]) === null || _a === void 0 ? void 0 : _a[contextPath]) === null || _b === void 0 ? void 0 : _b.$fast_context) ===
|
|
238
|
+
splitPath.at(-1)) {
|
|
239
|
+
self = true;
|
|
240
|
+
}
|
|
241
|
+
while (levelCount > 0 && !self) {
|
|
242
|
+
if (levelCount !== 1) {
|
|
243
|
+
parentContexts.push("parentContext");
|
|
259
244
|
}
|
|
260
245
|
else {
|
|
261
|
-
|
|
246
|
+
parentContexts.push("parent");
|
|
262
247
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
if (splitPath[index + 1] === "../") {
|
|
267
|
-
return "parentContext";
|
|
268
|
-
}
|
|
269
|
-
return "parent";
|
|
270
|
-
}
|
|
271
|
-
return pathItem;
|
|
272
|
-
});
|
|
248
|
+
levelCount--;
|
|
249
|
+
}
|
|
250
|
+
splitPath = [...parentContexts, ...splitPath];
|
|
273
251
|
return pathWithContextResolver(splitPath, self);
|
|
274
252
|
}
|
|
253
|
+
/**
|
|
254
|
+
* Creates a resolver function that can access properties from an object using a split path array
|
|
255
|
+
* @param splitPath - The dot syntax path split into an array of property names
|
|
256
|
+
* @param self - Whether the first item in the path refers to the item itself
|
|
257
|
+
* @returns A function that resolves the value from the given path on an accessible object
|
|
258
|
+
*/
|
|
275
259
|
function pathWithContextResolver(splitPath, self) {
|
|
276
|
-
const
|
|
277
|
-
if (self && !
|
|
260
|
+
const isInPreviousContext = splitPath[0] === "parent" || splitPath[0] === "parentContext";
|
|
261
|
+
if (self && !isInPreviousContext) {
|
|
278
262
|
if (splitPath.length > 1) {
|
|
279
263
|
splitPath = splitPath.slice(1);
|
|
280
264
|
}
|
|
@@ -284,7 +268,7 @@ function pathWithContextResolver(splitPath, self) {
|
|
|
284
268
|
};
|
|
285
269
|
}
|
|
286
270
|
}
|
|
287
|
-
if (
|
|
271
|
+
if (isInPreviousContext) {
|
|
288
272
|
return (accessibleObject, context) => {
|
|
289
273
|
return splitPath.reduce((previousAccessors, pathItem) => {
|
|
290
274
|
return previousAccessors === null || previousAccessors === void 0 ? void 0 : previousAccessors[pathItem];
|
|
@@ -297,40 +281,23 @@ function pathWithContextResolver(splitPath, self) {
|
|
|
297
281
|
}, accessibleObject);
|
|
298
282
|
};
|
|
299
283
|
}
|
|
300
|
-
export function bindingResolver(
|
|
301
|
-
|
|
302
|
-
if (
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
contextPath,
|
|
308
|
-
type,
|
|
309
|
-
level,
|
|
310
|
-
rootPath: null,
|
|
311
|
-
context: null,
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
return pathResolver(path, self);
|
|
315
|
-
}
|
|
316
|
-
export function expressionResolver(self, expression, parentContext, level, observerMap) {
|
|
317
|
-
// Cache paths from expression during template processing when ObserverMap is provided
|
|
318
|
-
if (observerMap) {
|
|
319
|
-
const paths = extractPathsFromChainedExpression(expression);
|
|
320
|
-
paths.forEach(path => {
|
|
321
|
-
observerMap.cachePathWithContext({
|
|
322
|
-
path,
|
|
323
|
-
self,
|
|
284
|
+
export function bindingResolver(rootPropertyName, path, parentContext, type, schema, currentContext, level) {
|
|
285
|
+
rootPropertyName = getRootPropertyName(rootPropertyName, path, currentContext, type);
|
|
286
|
+
if (type !== "event" && rootPropertyName !== null) {
|
|
287
|
+
schema.addPath({
|
|
288
|
+
pathConfig: {
|
|
289
|
+
type,
|
|
290
|
+
currentContext,
|
|
324
291
|
parentContext,
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
rootPath: null,
|
|
329
|
-
context: null,
|
|
330
|
-
});
|
|
292
|
+
path,
|
|
293
|
+
},
|
|
294
|
+
rootPropertyName,
|
|
331
295
|
});
|
|
332
296
|
}
|
|
333
|
-
return (
|
|
297
|
+
return pathResolver(path, currentContext, level, schema.getSchema(rootPropertyName));
|
|
298
|
+
}
|
|
299
|
+
export function expressionResolver(rootPropertyName, expression, parentContext, level, schema) {
|
|
300
|
+
return (x, c) => resolveChainedExpression(x, c, level, parentContext || null, expression, schema.getSchema(rootPropertyName));
|
|
334
301
|
}
|
|
335
302
|
/**
|
|
336
303
|
* Extracts all paths from a ChainedExpression, including nested expressions
|
|
@@ -358,7 +325,8 @@ export function extractPathsFromChainedExpression(chainedExpression) {
|
|
|
358
325
|
}
|
|
359
326
|
/**
|
|
360
327
|
* Determine if the operand is a value (boolean, number, string) or an accessor.
|
|
361
|
-
* @param operand
|
|
328
|
+
* @param operand - The string to evaluate as either a literal value or property accessor
|
|
329
|
+
* @returns An object containing the parsed value and whether it represents a literal value
|
|
362
330
|
*/
|
|
363
331
|
function isOperandValue(operand) {
|
|
364
332
|
try {
|
|
@@ -377,10 +345,10 @@ function isOperandValue(operand) {
|
|
|
377
345
|
}
|
|
378
346
|
}
|
|
379
347
|
/**
|
|
380
|
-
* Evaluates parts of an expression chain
|
|
381
|
-
* @param parts Each part of an expression chain
|
|
382
|
-
* @param operator The operator
|
|
383
|
-
* @returns
|
|
348
|
+
* Evaluates parts of an expression chain and chains them with the specified operator
|
|
349
|
+
* @param parts - Each part of an expression chain to be evaluated
|
|
350
|
+
* @param operator - The logical operator used to chain the expression parts
|
|
351
|
+
* @returns A ChainedExpression object representing the linked expressions, or void if no valid expressions found
|
|
384
352
|
*/
|
|
385
353
|
function evaluatePartsInExpressionChain(parts, operator) {
|
|
386
354
|
// Process each part recursively and chain them with ||
|
|
@@ -441,6 +409,11 @@ export function getExpressionChain(value) {
|
|
|
441
409
|
}
|
|
442
410
|
return void 0;
|
|
443
411
|
}
|
|
412
|
+
/**
|
|
413
|
+
* Parses a binding value string into an Expression object
|
|
414
|
+
* @param value - The binding string value to parse (e.g., "!condition", "foo == bar", "property")
|
|
415
|
+
* @returns An Expression object containing the operator, operands, and whether operands are literal values
|
|
416
|
+
*/
|
|
444
417
|
function getExpression(value) {
|
|
445
418
|
if (value[0] === "!") {
|
|
446
419
|
const left = value.slice(1);
|
|
@@ -477,62 +450,77 @@ function getExpression(value) {
|
|
|
477
450
|
};
|
|
478
451
|
}
|
|
479
452
|
/**
|
|
480
|
-
* Resolve a single expression
|
|
481
|
-
* @param x - The context
|
|
482
|
-
* @param c - The parent context
|
|
483
|
-
* @param
|
|
484
|
-
* @param
|
|
485
|
-
* @
|
|
453
|
+
* Resolve a single expression by evaluating its operator and operands
|
|
454
|
+
* @param x - The current data context
|
|
455
|
+
* @param c - The parent context for accessing parent scope data
|
|
456
|
+
* @param level - The nesting level for context resolution
|
|
457
|
+
* @param contextPath - The current context path for property resolution
|
|
458
|
+
* @param expression - The expression object to evaluate
|
|
459
|
+
* @param rootSchema - The root JSON schema for data validation and navigation
|
|
460
|
+
* @returns The resolved value of the expression
|
|
486
461
|
*/
|
|
487
|
-
function resolveExpression(x, c,
|
|
462
|
+
function resolveExpression(x, c, level, contextPath, expression, rootSchema) {
|
|
488
463
|
const { operator, left, right, rightIsValue } = expression;
|
|
489
464
|
switch (operator) {
|
|
490
465
|
case "!":
|
|
491
|
-
return !pathResolver(left,
|
|
466
|
+
return !pathResolver(left, contextPath, level, rootSchema)(x, c);
|
|
492
467
|
case "==":
|
|
493
|
-
return (pathResolver(left,
|
|
494
|
-
(rightIsValue
|
|
468
|
+
return (pathResolver(left, contextPath, level, rootSchema)(x, c) ==
|
|
469
|
+
(rightIsValue
|
|
470
|
+
? right
|
|
471
|
+
: pathResolver(right, contextPath, level, rootSchema)(x, c)));
|
|
495
472
|
case "!=":
|
|
496
|
-
return (pathResolver(left,
|
|
497
|
-
(rightIsValue
|
|
473
|
+
return (pathResolver(left, contextPath, level, rootSchema)(x, c) !=
|
|
474
|
+
(rightIsValue
|
|
475
|
+
? right
|
|
476
|
+
: pathResolver(right, contextPath, level, rootSchema)(x, c)));
|
|
498
477
|
case ">=":
|
|
499
|
-
return (pathResolver(left,
|
|
500
|
-
(rightIsValue
|
|
478
|
+
return (pathResolver(left, contextPath, level, rootSchema)(x, c) >=
|
|
479
|
+
(rightIsValue
|
|
480
|
+
? right
|
|
481
|
+
: pathResolver(right, contextPath, level, rootSchema)(x, c)));
|
|
501
482
|
case ">":
|
|
502
|
-
return (pathResolver(left,
|
|
503
|
-
(rightIsValue
|
|
483
|
+
return (pathResolver(left, contextPath, level, rootSchema)(x, c) >
|
|
484
|
+
(rightIsValue
|
|
485
|
+
? right
|
|
486
|
+
: pathResolver(right, contextPath, level, rootSchema)(x, c)));
|
|
504
487
|
case "<=":
|
|
505
|
-
return (pathResolver(left,
|
|
506
|
-
(rightIsValue
|
|
488
|
+
return (pathResolver(left, contextPath, level, rootSchema)(x, c) <=
|
|
489
|
+
(rightIsValue
|
|
490
|
+
? right
|
|
491
|
+
: pathResolver(right, contextPath, level, rootSchema)(x, c)));
|
|
507
492
|
case "<":
|
|
508
|
-
return (pathResolver(left,
|
|
509
|
-
(rightIsValue
|
|
493
|
+
return (pathResolver(left, contextPath, level, rootSchema)(x, c) <
|
|
494
|
+
(rightIsValue
|
|
495
|
+
? right
|
|
496
|
+
: pathResolver(right, contextPath, level, rootSchema)(x, c)));
|
|
510
497
|
default:
|
|
511
|
-
return pathResolver(left,
|
|
498
|
+
return pathResolver(left, contextPath, level, rootSchema)(x, c);
|
|
512
499
|
}
|
|
513
500
|
}
|
|
514
501
|
/**
|
|
515
|
-
* Resolve a chained expression
|
|
516
|
-
* @param x - The context
|
|
517
|
-
* @param c - The parent context
|
|
518
|
-
* @param
|
|
519
|
-
* @param
|
|
520
|
-
* @param
|
|
521
|
-
* @
|
|
502
|
+
* Resolve a chained expression by evaluating expressions linked with logical operators
|
|
503
|
+
* @param x - The current data context
|
|
504
|
+
* @param c - The parent context for accessing parent scope data
|
|
505
|
+
* @param level - The nesting level for context resolution
|
|
506
|
+
* @param contextPath - The current context path for property resolution
|
|
507
|
+
* @param expression - The chained expression object containing linked expressions
|
|
508
|
+
* @param rootSchema - The root JSON schema for data validation and navigation
|
|
509
|
+
* @returns The resolved boolean result of the chained expression
|
|
522
510
|
*/
|
|
523
|
-
function resolveChainedExpression(x, c,
|
|
511
|
+
function resolveChainedExpression(x, c, level, contextPath, expression, rootSchema) {
|
|
524
512
|
if (expression.next) {
|
|
525
513
|
switch (expression.next.operator) {
|
|
526
514
|
case "&&":
|
|
527
515
|
case "&&":
|
|
528
|
-
return (resolveExpression(x, c,
|
|
529
|
-
resolveChainedExpression(x, c,
|
|
516
|
+
return (resolveExpression(x, c, level, contextPath, expression.expression, rootSchema) &&
|
|
517
|
+
resolveChainedExpression(x, c, level, contextPath, expression.next, rootSchema));
|
|
530
518
|
case "||":
|
|
531
|
-
return (resolveExpression(x, c,
|
|
532
|
-
resolveChainedExpression(x, c,
|
|
519
|
+
return (resolveExpression(x, c, level, contextPath, expression.expression, rootSchema) ||
|
|
520
|
+
resolveChainedExpression(x, c, level, contextPath, expression.next, rootSchema));
|
|
533
521
|
}
|
|
534
522
|
}
|
|
535
|
-
return resolveExpression(x, c,
|
|
523
|
+
return resolveExpression(x, c, level, contextPath, expression.expression, rootSchema);
|
|
536
524
|
}
|
|
537
525
|
/**
|
|
538
526
|
* This is the transform utility for rationalizing declarative HTML syntax
|
|
@@ -568,44 +556,35 @@ export function transformInnerHTML(innerHTML, index = 0) {
|
|
|
568
556
|
* @param self - Where the first item in the path path refers to the item itself (used by repeat).
|
|
569
557
|
* @param chainedExpression - The chained expression which includes the expression and the next expression
|
|
570
558
|
* if there is another in the chain
|
|
571
|
-
* @param observerMap - Optional ObserverMap instance for caching paths during template processing
|
|
572
559
|
* @returns - A binding that resolves the chained expression logic
|
|
573
560
|
*/
|
|
574
|
-
export function resolveWhen(
|
|
575
|
-
const binding = expressionResolver(
|
|
561
|
+
export function resolveWhen(rootPropertyName, expression, parentContext, level, schema) {
|
|
562
|
+
const binding = expressionResolver(rootPropertyName, expression, parentContext, level, schema);
|
|
576
563
|
return (x, c) => binding(x, c);
|
|
577
564
|
}
|
|
578
565
|
/**
|
|
579
|
-
*
|
|
566
|
+
* Determines the data type of the provided data
|
|
567
|
+
* @param data - The data to analyze
|
|
568
|
+
* @returns "array" for arrays, "object" for non-null objects, "primitive" for other types
|
|
580
569
|
*/
|
|
581
|
-
function getDataType(
|
|
582
|
-
if (Array.isArray(
|
|
570
|
+
function getDataType(data) {
|
|
571
|
+
if (Array.isArray(data))
|
|
583
572
|
return "array";
|
|
584
|
-
if (typeof
|
|
573
|
+
if (typeof data === "object" && data !== null)
|
|
585
574
|
return "object";
|
|
586
575
|
return "primitive";
|
|
587
576
|
}
|
|
588
577
|
/**
|
|
589
|
-
*
|
|
590
|
-
* @param
|
|
591
|
-
* @
|
|
578
|
+
* Assigns Observable properties to items in an array and sets up change notifications
|
|
579
|
+
* @param proxiedData - The array data to make observable
|
|
580
|
+
* @param schema - The schema defining the structure of array items
|
|
581
|
+
* @param rootSchema - The root schema for the entire data structure
|
|
582
|
+
* @returns The array with observable properties and change notifications
|
|
592
583
|
*/
|
|
593
|
-
|
|
594
|
-
return path.split(".")[0];
|
|
595
|
-
}
|
|
596
|
-
function sortByDeepestNestingItem(first, second) {
|
|
597
|
-
const firstRelativePathLength = first.length;
|
|
598
|
-
const secondRelativePathLength = second.length;
|
|
599
|
-
return firstRelativePathLength > secondRelativePathLength
|
|
600
|
-
? -1
|
|
601
|
-
: secondRelativePathLength > firstRelativePathLength
|
|
602
|
-
? 1
|
|
603
|
-
: 0;
|
|
604
|
-
}
|
|
605
|
-
function assignObservablesToArray(proxiedData, cachePath) {
|
|
584
|
+
function assignObservablesToArray(proxiedData, schema, rootSchema) {
|
|
606
585
|
const data = proxiedData.map((item) => {
|
|
607
586
|
const originalItem = Object.assign({}, item);
|
|
608
|
-
assignProxyToItemsInArray(item, originalItem,
|
|
587
|
+
assignProxyToItemsInArray(item, originalItem, schema, rootSchema);
|
|
609
588
|
return Object.assign(item, originalItem);
|
|
610
589
|
});
|
|
611
590
|
Observable.getNotifier(data).subscribe({
|
|
@@ -615,7 +594,7 @@ function assignObservablesToArray(proxiedData, cachePath) {
|
|
|
615
594
|
for (let i = arg.addedCount - 1; i >= 0; i--) {
|
|
616
595
|
const item = subject[arg.index + i];
|
|
617
596
|
const originalItem = Object.assign({}, item);
|
|
618
|
-
assignProxyToItemsInArray(item, originalItem,
|
|
597
|
+
assignProxyToItemsInArray(item, originalItem, schema, rootSchema);
|
|
619
598
|
return Object.assign(item, originalItem);
|
|
620
599
|
}
|
|
621
600
|
}
|
|
@@ -624,73 +603,104 @@ function assignObservablesToArray(proxiedData, cachePath) {
|
|
|
624
603
|
});
|
|
625
604
|
return data;
|
|
626
605
|
}
|
|
627
|
-
|
|
606
|
+
/**
|
|
607
|
+
* Extracts the definition name from a JSON Schema $ref property
|
|
608
|
+
* @param defName - The $ref string (e.g., "#/$defs/MyType")
|
|
609
|
+
* @returns The definition name (e.g., "MyType")
|
|
610
|
+
*/
|
|
611
|
+
function getDefFromRef(defName) {
|
|
612
|
+
const splitName = defName.split("/");
|
|
613
|
+
return splitName.at(-1);
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Assign observables to data
|
|
617
|
+
* @param schema - The schema
|
|
618
|
+
* @param rootSchema - The root schema mapping to the root property
|
|
619
|
+
* @param data - The data
|
|
620
|
+
* @param target - The target custom element
|
|
621
|
+
* @param rootProperty - The root property
|
|
622
|
+
* @returns
|
|
623
|
+
*/
|
|
624
|
+
export function assignObservables(schema, rootSchema, data, target, rootProperty) {
|
|
625
|
+
var _a;
|
|
628
626
|
const dataType = getDataType(data);
|
|
629
627
|
let proxiedData = data;
|
|
630
628
|
switch (dataType) {
|
|
631
629
|
case "array": {
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
}
|
|
630
|
+
const context = getDefFromRef(schema[refPropertyName]);
|
|
631
|
+
proxiedData = assignObservablesToArray(proxiedData, (_a = rootSchema[defsPropertyName]) === null || _a === void 0 ? void 0 : _a[context], rootSchema);
|
|
635
632
|
break;
|
|
636
633
|
}
|
|
637
634
|
case "object": {
|
|
638
|
-
|
|
639
|
-
const relativePaths = Object.values(cachePath.paths)
|
|
640
|
-
.map((value) => {
|
|
641
|
-
return value.relativePath.split(".").slice(1); // the first item is the root path
|
|
642
|
-
})
|
|
643
|
-
.sort(sortByDeepestNestingItem);
|
|
644
|
-
for (const relativePath of relativePaths) {
|
|
645
|
-
proxiedData = assignProxyToItemsInObject(relativePath, target, rootProperty, proxiedData, cachePath);
|
|
646
|
-
}
|
|
647
|
-
}
|
|
635
|
+
proxiedData = assignProxyToItemsInObject(target, rootProperty, proxiedData, schema, rootSchema);
|
|
648
636
|
break;
|
|
649
637
|
}
|
|
650
638
|
}
|
|
651
639
|
return proxiedData;
|
|
652
640
|
}
|
|
653
|
-
|
|
641
|
+
/**
|
|
642
|
+
* Assign a proxy to items in an array
|
|
643
|
+
* @param item - The array item to proxy
|
|
644
|
+
* @param originalItem - The original array item
|
|
645
|
+
* @param schema - The schema mapping to the items in the array
|
|
646
|
+
* @param rootSchema - The root schema assigned to the root property
|
|
647
|
+
*/
|
|
648
|
+
function assignProxyToItemsInArray(item, originalItem, schema, rootSchema) {
|
|
654
649
|
const itemProperties = Object.keys(item);
|
|
655
650
|
itemProperties.forEach(key => {
|
|
656
651
|
Observable.defineProperty(item, key);
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
return pathItem.relativePath.startsWith(`${cachePath.context}.${key}.`);
|
|
660
|
-
}
|
|
661
|
-
return false;
|
|
662
|
-
})
|
|
663
|
-
.map(value => {
|
|
664
|
-
return value.relativePath.split(".").slice(2); // the first item is the context, the next is the property
|
|
665
|
-
})
|
|
666
|
-
.sort(sortByDeepestNestingItem);
|
|
667
|
-
for (const relativePath of relativePaths) {
|
|
668
|
-
originalItem[key] = assignProxyToItemsInObject(relativePath, item, key, originalItem[key], cachePath);
|
|
652
|
+
if (originalItem[key] && schema && schema.properties) {
|
|
653
|
+
originalItem[key] = assignProxyToItemsInObject(item, key, originalItem[key], schema.properties[key], rootSchema);
|
|
669
654
|
}
|
|
670
655
|
});
|
|
671
656
|
}
|
|
672
|
-
|
|
657
|
+
/**
|
|
658
|
+
* Assign a proxy to items in an object
|
|
659
|
+
* @param target - The target custom element
|
|
660
|
+
* @param rootProperty - The root property
|
|
661
|
+
* @param data - The data to proxy
|
|
662
|
+
* @param schema - The schema for the data
|
|
663
|
+
* @param rootSchema - The root schema for the root property
|
|
664
|
+
* @returns a Proxy
|
|
665
|
+
*/
|
|
666
|
+
function assignProxyToItemsInObject(target, rootProperty, data, schema, rootSchema) {
|
|
667
|
+
var _a;
|
|
673
668
|
const type = getDataType(data);
|
|
674
669
|
let proxiedData = data;
|
|
675
|
-
if (type === "object") {
|
|
670
|
+
if (type === "object" && (schema === null || schema === void 0 ? void 0 : schema.properties)) {
|
|
676
671
|
// navigate through all items in the object
|
|
677
|
-
|
|
672
|
+
Object.keys(schema.properties).forEach(property => {
|
|
673
|
+
if (proxiedData[property] && schema && schema.properties) {
|
|
674
|
+
proxiedData[property] = assignProxyToItemsInObject(target, rootProperty, proxiedData[property], schema.properties[property], rootSchema);
|
|
675
|
+
}
|
|
676
|
+
});
|
|
678
677
|
// assign a Proxy to the object
|
|
679
|
-
proxiedData = assignProxy(
|
|
678
|
+
proxiedData = assignProxy(schema, rootSchema, target, rootProperty, data);
|
|
680
679
|
}
|
|
681
680
|
else if (type === "array") {
|
|
682
|
-
|
|
683
|
-
|
|
681
|
+
const context = getDefFromRef(schema.items[refPropertyName]);
|
|
682
|
+
const definition = (_a = rootSchema[defsPropertyName]) === null || _a === void 0 ? void 0 : _a[context];
|
|
683
|
+
if ((definition === null || definition === void 0 ? void 0 : definition.type) === "object") {
|
|
684
|
+
proxiedData = assignObservablesToArray(proxiedData, definition, rootSchema);
|
|
684
685
|
}
|
|
685
686
|
}
|
|
686
687
|
return proxiedData;
|
|
687
688
|
}
|
|
688
|
-
|
|
689
|
+
/**
|
|
690
|
+
* Assign a proxy to an object
|
|
691
|
+
* @param schema - The current schema
|
|
692
|
+
* @param rootSchema - The root schema for the root property
|
|
693
|
+
* @param target - The target custom element
|
|
694
|
+
* @param rootProperty - The root property
|
|
695
|
+
* @param object - The object to assign the proxy to
|
|
696
|
+
* @returns Proxy object
|
|
697
|
+
*/
|
|
698
|
+
export function assignProxy(schema, rootSchema, target, rootProperty, object) {
|
|
689
699
|
if (object.$isProxy === undefined) {
|
|
690
700
|
// Create a proxy for the object that triggers Observable.notify on mutations
|
|
691
701
|
return new Proxy(object, {
|
|
692
702
|
set: (obj, prop, value) => {
|
|
693
|
-
obj[prop] = assignObservables(
|
|
703
|
+
obj[prop] = assignObservables(schema, rootSchema, value, target, rootProperty);
|
|
694
704
|
// Trigger notification for property changes
|
|
695
705
|
Observable.notify(target, rootProperty);
|
|
696
706
|
return true;
|
|
@@ -714,3 +724,16 @@ export function assignProxy(cachePath, target, rootProperty, object) {
|
|
|
714
724
|
}
|
|
715
725
|
return object;
|
|
716
726
|
}
|
|
727
|
+
/**
|
|
728
|
+
* Get the root property name
|
|
729
|
+
* @param rootPropertyName - The root property
|
|
730
|
+
* @param path - The dot syntax path
|
|
731
|
+
* @param context - The context created by a repeat
|
|
732
|
+
* @param type - The type of path binding
|
|
733
|
+
* @returns
|
|
734
|
+
*/
|
|
735
|
+
export function getRootPropertyName(rootPropertyName, path, context, type) {
|
|
736
|
+
return (rootPropertyName === null || context === null) && type !== "event"
|
|
737
|
+
? path.split(".")[0]
|
|
738
|
+
: rootPropertyName;
|
|
739
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __awaiter } from "tslib";
|
|
2
2
|
import { expect, test } from "@playwright/test";
|
|
3
|
-
import { getNextBehavior,
|
|
3
|
+
import { getNextBehavior, getIndexOfNextMatchingTag, pathResolver, transformInnerHTML, getExpressionChain, extractPathsFromChainedExpression, } from "./utilities.js";
|
|
4
4
|
test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
5
5
|
test.describe("content", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
6
6
|
test("get the next content binding", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -108,29 +108,6 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
108
108
|
expect(result === null || result === void 0 ? void 0 : result.closingEndIndex).toEqual(21);
|
|
109
109
|
}));
|
|
110
110
|
}));
|
|
111
|
-
test.describe("partials", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
112
|
-
test("get a single partial", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
113
|
-
const partialContent = "{{text}}";
|
|
114
|
-
const partial = `<f-partial id="foo">${partialContent}</f-partial>`;
|
|
115
|
-
const allPartials = getAllPartials(partial);
|
|
116
|
-
expect(allPartials.foo.innerHTML).toEqual(partialContent);
|
|
117
|
-
expect(allPartials.foo.startIndex).toEqual(20);
|
|
118
|
-
expect(allPartials.foo.endIndex).toEqual(28);
|
|
119
|
-
}));
|
|
120
|
-
test("get multiple partials", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
121
|
-
const partial1Content = "{{text}}";
|
|
122
|
-
const partial2Content = "{{othertext}}";
|
|
123
|
-
const partial1 = `<f-partial id="foo">${partial1Content}</f-partial>`;
|
|
124
|
-
const partial2 = `<f-partial id="foobar">${partial2Content}</f-partial>`;
|
|
125
|
-
const allPartials = getAllPartials(`${partial1}${partial2}`);
|
|
126
|
-
expect(allPartials.foo.innerHTML).toEqual(partial1Content);
|
|
127
|
-
expect(allPartials.foo.startIndex).toEqual(20);
|
|
128
|
-
expect(allPartials.foo.endIndex).toEqual(28);
|
|
129
|
-
expect(allPartials.foobar.innerHTML).toEqual(partial2Content);
|
|
130
|
-
expect(allPartials.foobar.startIndex).toEqual(63);
|
|
131
|
-
expect(allPartials.foobar.endIndex).toEqual(76);
|
|
132
|
-
}));
|
|
133
|
-
}));
|
|
134
111
|
test.describe("getIndexOfNextMatchingTag", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
135
112
|
test("should resolve a single tag", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
136
113
|
const index = getIndexOfNextMatchingTag(`<div>Hello world</div>`, `<div`, `</div>`, 0);
|
|
@@ -151,19 +128,19 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
151
128
|
}));
|
|
152
129
|
test.describe("pathResolver", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
153
130
|
test("should resolve a path with no nesting", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
154
|
-
expect(pathResolver("foo")({ foo: "bar" }, {})).toEqual("bar");
|
|
131
|
+
expect(pathResolver("foo", null, 0, {})({ foo: "bar" }, {})).toEqual("bar");
|
|
155
132
|
}));
|
|
156
133
|
test("should resolve a path with nesting", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
157
|
-
expect(pathResolver("foo.bar.bat")({ foo: { bar: { bat: "baz" } } }, {})).toEqual("baz");
|
|
134
|
+
expect(pathResolver("foo.bar.bat", null, 0, {})({ foo: { bar: { bat: "baz" } } }, {})).toEqual("baz");
|
|
158
135
|
}));
|
|
159
136
|
test("should resolve a path with no nesting and self reference", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
160
|
-
expect(pathResolver("foo",
|
|
137
|
+
expect(pathResolver("foo", "foo", 0, {})("bar", {})).toEqual("bar");
|
|
161
138
|
}));
|
|
162
139
|
test("should resolve a path with nesting and self reference", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
163
|
-
expect(pathResolver("foo.bar.bat",
|
|
140
|
+
expect(pathResolver("foo.bar.bat", "foo", 0, {})({ bar: { bat: "baz" } }, {})).toEqual("baz");
|
|
164
141
|
}));
|
|
165
142
|
test("should resolve a path with context", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
166
|
-
expect(pathResolver("
|
|
143
|
+
expect(pathResolver("foo", "parent", 1, {})({}, { parent: { foo: "bar" } })).toEqual("bar");
|
|
167
144
|
}));
|
|
168
145
|
}));
|
|
169
146
|
test.describe("transformInnerHTML", () => __awaiter(void 0, void 0, void 0, function* () {
|