@microsoft/fast-html 1.0.0-alpha.40 → 1.0.0-alpha.42
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 +25 -2
- package/dist/dts/components/syntax.d.ts +4 -0
- package/dist/dts/components/utilities.d.ts +4 -2
- package/dist/esm/components/syntax.js +17 -0
- package/dist/esm/components/template.js +57 -32
- package/dist/esm/components/utilities.js +52 -28
- package/dist/esm/components/utilities.spec.js +55 -49
- package/package.json +4 -6
package/README.md
CHANGED
|
@@ -313,13 +313,36 @@ Where the right operand can be either a reference to a value (string e.g. `{{foo
|
|
|
313
313
|
<ul><f-repeat value="{{item in list}}"><li>{{item}}</li></f-repeat></ul>
|
|
314
314
|
```
|
|
315
315
|
|
|
316
|
-
|
|
316
|
+
Bindings inside `<f-repeat>` without a context prefix resolve to the custom element. For example, `{{title}}` below resolves to the host element's `title` property:
|
|
317
317
|
|
|
318
|
-
Example:
|
|
319
318
|
```html
|
|
320
319
|
<ul><f-repeat value="{{item in list}}"><li>{{item}} - {{title}}</li></f-repeat></ul>
|
|
321
320
|
```
|
|
322
321
|
|
|
322
|
+
#### Execution Context Access
|
|
323
|
+
|
|
324
|
+
In imperative fast-element templates, every binding expression receives both the data source and the execution context: `${(x, c) => c.parent.handleClick(c.event)}`. Declarative `<f-template>` expressions can access the same execution context using the `$c` prefix.
|
|
325
|
+
|
|
326
|
+
This is particularly useful inside `<f-repeat>`, where `$c.parent` refers to the parent view-model (typically the host element) and `$c.event` provides the DOM event.
|
|
327
|
+
|
|
328
|
+
Event handler with context access:
|
|
329
|
+
|
|
330
|
+
```html
|
|
331
|
+
<f-repeat value="{{item in items}}">
|
|
332
|
+
<button @click="{$c.parent.handleItemClick($c.event)}">{{item.name}}</button>
|
|
333
|
+
</f-repeat>
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Conditional rendering using a host property inside a repeat:
|
|
337
|
+
|
|
338
|
+
```html
|
|
339
|
+
<f-repeat value="{{item in items}}">
|
|
340
|
+
<f-when value="{{$c.parent.showNames}}">
|
|
341
|
+
<span>{{item.name}}</span>
|
|
342
|
+
</f-when>
|
|
343
|
+
</f-repeat>
|
|
344
|
+
```
|
|
345
|
+
|
|
323
346
|
#### Unescaped HTML
|
|
324
347
|
|
|
325
348
|
You can add unescaped HTML using triple braces, this will create an additional `div` element as the HTML needs an element to bind to. Where possible it is advisable to not use unescaped HTML and instead use other binding techniques.
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default syntax for FAST declarative templates
|
|
3
|
+
*/
|
|
4
|
+
export declare const attributeDirectivePrefix: string, clientSideCloseExpression: string, clientSideOpenExpression: string, closeExpression: string, executionContextAccessor: string, openExpression: string, repeatDirectiveClose: string, repeatDirectiveOpen: string, unescapedCloseExpression: string, unescapedOpenExpression: string, whenDirectiveClose: string, whenDirectiveOpen: string;
|
|
@@ -40,6 +40,7 @@ export interface ChildrenMap {
|
|
|
40
40
|
customElementName: string;
|
|
41
41
|
attributeName: string;
|
|
42
42
|
}
|
|
43
|
+
export declare const contextPrefixDot: string;
|
|
43
44
|
declare const LogicalOperator: {
|
|
44
45
|
AND: string;
|
|
45
46
|
OR: string;
|
|
@@ -115,13 +116,14 @@ export declare function getExpressionChain(value: string): ChainedExpression | v
|
|
|
115
116
|
*/
|
|
116
117
|
export declare function transformInnerHTML(innerHTML: string, index?: number): string;
|
|
117
118
|
/**
|
|
118
|
-
* Resolves
|
|
119
|
+
* Resolves boolean logic
|
|
120
|
+
* used for f-when and boolean attributes
|
|
119
121
|
* @param self - Where the first item in the path path refers to the item itself (used by repeat).
|
|
120
122
|
* @param chainedExpression - The chained expression which includes the expression and the next expression
|
|
121
123
|
* if there is another in the chain
|
|
122
124
|
* @returns - A binding that resolves the chained expression logic
|
|
123
125
|
*/
|
|
124
|
-
export declare function
|
|
126
|
+
export declare function getBooleanBinding(rootPropertyName: string | null, expression: ChainedExpression, parentContext: string | null, level: number, schema: Schema): (x: boolean, c: any) => any;
|
|
125
127
|
/**
|
|
126
128
|
* Find a definition
|
|
127
129
|
* This may exist as a $ref at the root or as a $ref in any anyOf or not at all
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default syntax for FAST declarative templates
|
|
3
|
+
*/
|
|
4
|
+
export const { attributeDirectivePrefix, clientSideCloseExpression, clientSideOpenExpression, closeExpression, executionContextAccessor, openExpression, repeatDirectiveClose, repeatDirectiveOpen, unescapedCloseExpression, unescapedOpenExpression, whenDirectiveClose, whenDirectiveOpen, } = {
|
|
5
|
+
attributeDirectivePrefix: "f-",
|
|
6
|
+
clientSideCloseExpression: "}",
|
|
7
|
+
clientSideOpenExpression: "{",
|
|
8
|
+
closeExpression: "}}",
|
|
9
|
+
executionContextAccessor: "$c",
|
|
10
|
+
openExpression: "{{",
|
|
11
|
+
unescapedCloseExpression: "}}}",
|
|
12
|
+
unescapedOpenExpression: "{{{",
|
|
13
|
+
repeatDirectiveClose: "</f-repeat>",
|
|
14
|
+
repeatDirectiveOpen: "<f-repeat",
|
|
15
|
+
whenDirectiveClose: "</f-when>",
|
|
16
|
+
whenDirectiveOpen: "<f-when",
|
|
17
|
+
};
|
|
@@ -3,7 +3,7 @@ import { attr, children, elements, FAST, FASTElement, FASTElementDefinition, fas
|
|
|
3
3
|
import "@microsoft/fast-element/install-hydratable-view-templates.js";
|
|
4
4
|
import { ObserverMap } from "./observer-map.js";
|
|
5
5
|
import { Schema } from "./schema.js";
|
|
6
|
-
import { bindingResolver, getExpressionChain, getNextBehavior, getRootPropertyName,
|
|
6
|
+
import { bindingResolver, contextPrefixDot, getBooleanBinding, getExpressionChain, getNextBehavior, getRootPropertyName, transformInnerHTML, } from "./utilities.js";
|
|
7
7
|
/**
|
|
8
8
|
* Values for the observerMap element option.
|
|
9
9
|
*/
|
|
@@ -69,24 +69,24 @@ class TemplateElement extends FASTElement {
|
|
|
69
69
|
}
|
|
70
70
|
this.schema = new Schema(name);
|
|
71
71
|
FASTElementDefinition.registerAsync(name).then(async (value) => {
|
|
72
|
-
var _a, _b,
|
|
72
|
+
var _a, _b, _d, _e, _f, _g, _h;
|
|
73
73
|
(_b = (_a = TemplateElement.lifecycleCallbacks).elementDidRegister) === null || _b === void 0 ? void 0 : _b.call(_a, name);
|
|
74
|
-
if (!((
|
|
74
|
+
if (!((_d = TemplateElement.elementOptions) === null || _d === void 0 ? void 0 : _d[name])) {
|
|
75
75
|
TemplateElement.setOptions(name);
|
|
76
76
|
}
|
|
77
|
-
if (((
|
|
77
|
+
if (((_e = TemplateElement.elementOptions[name]) === null || _e === void 0 ? void 0 : _e.observerMap) === "all") {
|
|
78
78
|
this.observerMap = new ObserverMap(value.prototype, this.schema);
|
|
79
79
|
}
|
|
80
80
|
const registeredFastElement = fastElementRegistry.getByType(value);
|
|
81
81
|
const template = this.getElementsByTagName("template").item(0);
|
|
82
82
|
if (template) {
|
|
83
83
|
// Callback: Before template has been evaluated and assigned
|
|
84
|
-
(
|
|
84
|
+
(_g = (_f = TemplateElement.lifecycleCallbacks).templateWillUpdate) === null || _g === void 0 ? void 0 : _g.call(_f, name);
|
|
85
85
|
const innerHTML = transformInnerHTML(this.innerHTML);
|
|
86
86
|
// Cache paths during template processing (pass undefined if observerMap is not available)
|
|
87
87
|
const { strings, values } = await this.resolveStringsAndValues(null, innerHTML, false, null, 0, this.schema, this.observerMap);
|
|
88
88
|
// Define the root properties cached in the observer map as observable (only if observerMap exists)
|
|
89
|
-
(
|
|
89
|
+
(_h = this.observerMap) === null || _h === void 0 ? void 0 : _h.defineProperties();
|
|
90
90
|
if (registeredFastElement) {
|
|
91
91
|
// Attach lifecycle callbacks to the definition before assigning template
|
|
92
92
|
// This allows the Observable notification to trigger the callbacks
|
|
@@ -140,7 +140,7 @@ class TemplateElement extends FASTElement {
|
|
|
140
140
|
switch (behaviorConfig.name) {
|
|
141
141
|
case "when": {
|
|
142
142
|
const expressionChain = getExpressionChain(behaviorConfig.value);
|
|
143
|
-
const whenLogic =
|
|
143
|
+
const whenLogic = getBooleanBinding(rootPropertyName, expressionChain, parentContext, level, schema);
|
|
144
144
|
const { strings, values } = await this.resolveStringsAndValues(rootPropertyName, innerHTML.slice(behaviorConfig.openingTagEndIndex, behaviorConfig.closingTagStartIndex), self, parentContext, level, schema, observerMap);
|
|
145
145
|
externalValues.push(when(whenLogic, this.resolveTemplateOrBehavior(strings, values)));
|
|
146
146
|
break;
|
|
@@ -215,32 +215,57 @@ class TemplateElement extends FASTElement {
|
|
|
215
215
|
}
|
|
216
216
|
case "attribute": {
|
|
217
217
|
strings.push(innerHTML.slice(0, behaviorConfig.openingStartIndex));
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
218
|
+
let attributeBinding;
|
|
219
|
+
switch (behaviorConfig.aspect) {
|
|
220
|
+
case "@": {
|
|
221
|
+
const bindingHTML = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex);
|
|
222
|
+
const openingParenthesis = bindingHTML.indexOf("(");
|
|
223
|
+
const closingParenthesis = bindingHTML.indexOf(")");
|
|
224
|
+
const propName = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex -
|
|
225
|
+
(closingParenthesis - openingParenthesis) -
|
|
226
|
+
1);
|
|
227
|
+
const type = "event";
|
|
228
|
+
rootPropertyName = getRootPropertyName(rootPropertyName, propName, parentContext, type);
|
|
229
|
+
const arg = bindingHTML.slice(openingParenthesis + 1, closingParenthesis);
|
|
230
|
+
const binding = bindingResolver(strings.join(""), rootPropertyName, propName, parentContext, type, schema, parentContext, level);
|
|
231
|
+
const isContextPath = propName.startsWith(contextPrefixDot);
|
|
232
|
+
const getOwner = isContextPath
|
|
233
|
+
? (_x, c) => {
|
|
234
|
+
const ownerPath = propName.split(".").slice(1, -1);
|
|
235
|
+
return ownerPath.reduce((prev, item) => prev === null || prev === void 0 ? void 0 : prev[item], c);
|
|
236
|
+
}
|
|
237
|
+
: (x, _c) => x;
|
|
238
|
+
attributeBinding = (x, c) => binding(x, c).bind(getOwner(x, c))(...(arg === "e" ? [c.event] : []), ...(arg !== "e" && arg !== ""
|
|
239
|
+
? [
|
|
240
|
+
bindingResolver(strings.join(""), rootPropertyName, arg, parentContext, type, schema, parentContext, level)(x, c),
|
|
241
|
+
]
|
|
242
|
+
: []));
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
case "?": {
|
|
246
|
+
const expression = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex);
|
|
247
|
+
const expressionChain = getExpressionChain(expression);
|
|
248
|
+
if (expressionChain === null || expressionChain === void 0 ? void 0 : expressionChain.expression.operator) {
|
|
249
|
+
attributeBinding = getBooleanBinding(rootPropertyName, expressionChain, parentContext, level, schema);
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
const propName = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex);
|
|
253
|
+
const type = "access";
|
|
254
|
+
rootPropertyName = getRootPropertyName(rootPropertyName, propName, parentContext, type);
|
|
255
|
+
const binding = bindingResolver(strings.join(""), rootPropertyName, propName, parentContext, type, schema, parentContext, level);
|
|
256
|
+
attributeBinding = (x, c) => binding(x, c);
|
|
257
|
+
}
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
default: {
|
|
261
|
+
const propName = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex);
|
|
262
|
+
const type = "access";
|
|
263
|
+
rootPropertyName = getRootPropertyName(rootPropertyName, propName, parentContext, type);
|
|
264
|
+
const binding = bindingResolver(strings.join(""), rootPropertyName, propName, parentContext, type, schema, parentContext, level);
|
|
265
|
+
attributeBinding = (x, c) => binding(x, c);
|
|
266
|
+
}
|
|
243
267
|
}
|
|
268
|
+
values.push(attributeBinding);
|
|
244
269
|
await this.resolveInnerHTML(rootPropertyName, innerHTML.slice(behaviorConfig.closingEndIndex, innerHTML.length), strings, values, self, parentContext, level, schema, observerMap);
|
|
245
270
|
break;
|
|
246
271
|
}
|
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
import { Observable } from "@microsoft/fast-element/observable.js";
|
|
2
2
|
import { defsPropertyName, fastContextMetaData, refPropertyName, Schema, } from "./schema.js";
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
const openContentBinding = "{{";
|
|
6
|
-
const closeContentBinding = "}}";
|
|
7
|
-
const openUnescapedBinding = "{{{";
|
|
8
|
-
const closeUnescapedBinding = "}}}";
|
|
9
|
-
const openTagStart = "<f-";
|
|
10
|
-
const tagEnd = ">";
|
|
11
|
-
const closeTagStart = "</f-";
|
|
12
|
-
const attributeDirectivePrefix = "f-";
|
|
3
|
+
import { attributeDirectivePrefix, clientSideCloseExpression, clientSideOpenExpression, closeExpression, executionContextAccessor, openExpression, repeatDirectiveClose, repeatDirectiveOpen, unescapedCloseExpression, unescapedOpenExpression, whenDirectiveClose, whenDirectiveOpen, } from "./syntax.js";
|
|
4
|
+
export const contextPrefixDot = `${executionContextAccessor}.`;
|
|
13
5
|
const startInnerHTMLDiv = `<div :innerHTML="{{`;
|
|
14
6
|
const startInnerHTMLDivLength = startInnerHTMLDiv.length;
|
|
15
7
|
const endInnerHTMLDiv = `}}"></div>`;
|
|
@@ -90,16 +82,23 @@ export function getIndexOfNextMatchingTag(openingTagStartSlice, openingTag, clos
|
|
|
90
82
|
* @returns DirectiveBehaviorConfig - A configuration object
|
|
91
83
|
*/
|
|
92
84
|
function getNextDirectiveBehavior(innerHTML) {
|
|
93
|
-
const
|
|
85
|
+
const whenIndex = innerHTML.indexOf(whenDirectiveOpen);
|
|
86
|
+
const repeatIndex = innerHTML.indexOf(repeatDirectiveOpen);
|
|
87
|
+
const isWhen = whenIndex !== -1 && (repeatIndex === -1 || whenIndex < repeatIndex);
|
|
88
|
+
let openingTag = repeatDirectiveOpen;
|
|
89
|
+
let closingTag = repeatDirectiveClose;
|
|
90
|
+
let directiveTag = "repeat";
|
|
91
|
+
let openingTagStartIndex = repeatIndex;
|
|
92
|
+
if (isWhen) {
|
|
93
|
+
openingTag = whenDirectiveOpen;
|
|
94
|
+
closingTag = whenDirectiveClose;
|
|
95
|
+
directiveTag = "when";
|
|
96
|
+
openingTagStartIndex = whenIndex;
|
|
97
|
+
}
|
|
94
98
|
const openingTagStartSlice = innerHTML.slice(openingTagStartIndex);
|
|
95
99
|
const openingTagEndIndex = // account for f-when which may include >= or > as operators, but will always include a condition attr
|
|
96
|
-
openingTagStartSlice.indexOf(`"
|
|
97
|
-
const directiveTag = innerHTML
|
|
98
|
-
.slice(openingTagStartIndex + 3, openingTagEndIndex - 1)
|
|
99
|
-
.split(" ")[0];
|
|
100
|
+
openingTagStartSlice.indexOf(`">`) + openingTagStartIndex + 2;
|
|
100
101
|
const directiveValue = getNextDataBindingBehavior(innerHTML);
|
|
101
|
-
const openingTag = `${openTagStart}${directiveTag}`;
|
|
102
|
-
const closingTag = `${closeTagStart}${directiveTag}${tagEnd}`;
|
|
103
102
|
const matchingCloseTagIndex = getIndexOfNextMatchingTag(openingTagStartSlice, openingTag, closingTag, openingTagStartIndex);
|
|
104
103
|
return {
|
|
105
104
|
type: "templateDirective",
|
|
@@ -185,14 +184,14 @@ function getContentDataBindingConfig(config) {
|
|
|
185
184
|
*/
|
|
186
185
|
function getIndexAndBindingTypeOfNextDataBindingBehavior(innerHTML) {
|
|
187
186
|
// {{{}}} binding
|
|
188
|
-
const openingUnescapedStartIndex = innerHTML.indexOf(
|
|
189
|
-
const closingUnescapedStartIndex = innerHTML.indexOf(
|
|
187
|
+
const openingUnescapedStartIndex = innerHTML.indexOf(unescapedOpenExpression);
|
|
188
|
+
const closingUnescapedStartIndex = innerHTML.indexOf(unescapedCloseExpression);
|
|
190
189
|
// {{}} binding
|
|
191
|
-
const openingContentStartIndex = innerHTML.indexOf(
|
|
192
|
-
const closingContentStartIndex = innerHTML.indexOf(
|
|
190
|
+
const openingContentStartIndex = innerHTML.indexOf(openExpression);
|
|
191
|
+
const closingContentStartIndex = innerHTML.indexOf(closeExpression);
|
|
193
192
|
// {} binding
|
|
194
|
-
const openingClientStartIndex = innerHTML.indexOf(
|
|
195
|
-
const closingClientStartIndex = innerHTML.indexOf(
|
|
193
|
+
const openingClientStartIndex = innerHTML.indexOf(clientSideOpenExpression);
|
|
194
|
+
const closingClientStartIndex = innerHTML.indexOf(clientSideCloseExpression);
|
|
196
195
|
if (openingUnescapedStartIndex !== -1 &&
|
|
197
196
|
openingUnescapedStartIndex <= openingContentStartIndex &&
|
|
198
197
|
openingUnescapedStartIndex <= openingClientStartIndex) {
|
|
@@ -251,8 +250,14 @@ export function getNextBehavior(innerHTML, offset = 0) {
|
|
|
251
250
|
while (true) {
|
|
252
251
|
const currentSlice = innerHTML.slice(offset);
|
|
253
252
|
// client side binding will capture all bindings starting with "{"
|
|
254
|
-
const dataBindingOpen = currentSlice.indexOf(
|
|
255
|
-
const
|
|
253
|
+
const dataBindingOpen = currentSlice.indexOf(clientSideOpenExpression);
|
|
254
|
+
const whenDirectiveIndex = currentSlice.indexOf(whenDirectiveOpen);
|
|
255
|
+
const repeatDirectiveIndex = currentSlice.indexOf(repeatDirectiveOpen);
|
|
256
|
+
const directiveBindingOpen = whenDirectiveIndex === -1
|
|
257
|
+
? repeatDirectiveIndex
|
|
258
|
+
: repeatDirectiveIndex === -1
|
|
259
|
+
? whenDirectiveIndex
|
|
260
|
+
: Math.min(whenDirectiveIndex, repeatDirectiveIndex);
|
|
256
261
|
const nextDataBindingBehavior = getNextDataBindingBehavior(currentSlice);
|
|
257
262
|
if (dataBindingOpen === -1 && directiveBindingOpen === -1) {
|
|
258
263
|
return null;
|
|
@@ -318,6 +323,13 @@ function isLegitimateClientSideBinding(result) {
|
|
|
318
323
|
export function pathResolver(path, contextPath, level, rootSchema) {
|
|
319
324
|
var _a, _b;
|
|
320
325
|
let splitPath = path.split(".");
|
|
326
|
+
// Explicit context access via executionContextAccessor — resolve directly from ExecutionContext
|
|
327
|
+
if (splitPath[0] === executionContextAccessor) {
|
|
328
|
+
const contextAccessPath = splitPath.slice(1);
|
|
329
|
+
return (_accessibleObject, context) => {
|
|
330
|
+
return contextAccessPath.reduce((prev, item) => prev === null || prev === void 0 ? void 0 : prev[item], context);
|
|
331
|
+
};
|
|
332
|
+
}
|
|
321
333
|
let levelCount = level;
|
|
322
334
|
let self = splitPath[0] === contextPath;
|
|
323
335
|
const parentContexts = [];
|
|
@@ -370,6 +382,13 @@ function pathWithContextResolver(splitPath, self) {
|
|
|
370
382
|
};
|
|
371
383
|
}
|
|
372
384
|
export function bindingResolver(previousString, rootPropertyName, path, parentContext, type, schema, currentContext, level) {
|
|
385
|
+
// Explicit context access — resolve from ExecutionContext, skip schema tracking
|
|
386
|
+
if (path.startsWith(contextPrefixDot)) {
|
|
387
|
+
const segments = path.split(".").slice(1);
|
|
388
|
+
return (_x, context) => {
|
|
389
|
+
return segments.reduce((prev, item) => prev === null || prev === void 0 ? void 0 : prev[item], context);
|
|
390
|
+
};
|
|
391
|
+
}
|
|
373
392
|
rootPropertyName = getRootPropertyName(rootPropertyName, path, currentContext, type);
|
|
374
393
|
if (type !== "event" && rootPropertyName !== null) {
|
|
375
394
|
const childrenMap = getChildrenMap(previousString);
|
|
@@ -391,6 +410,8 @@ export function expressionResolver(rootPropertyName, expression, parentContext,
|
|
|
391
410
|
if (rootPropertyName !== null) {
|
|
392
411
|
const paths = extractPathsFromChainedExpression(expression);
|
|
393
412
|
paths.forEach(path => {
|
|
413
|
+
if (path.startsWith(contextPrefixDot))
|
|
414
|
+
return;
|
|
394
415
|
schema.addPath({
|
|
395
416
|
pathConfig: {
|
|
396
417
|
type: "access",
|
|
@@ -443,7 +464,7 @@ function isOperandValue(operand) {
|
|
|
443
464
|
isValue: true,
|
|
444
465
|
};
|
|
445
466
|
}
|
|
446
|
-
catch
|
|
467
|
+
catch {
|
|
447
468
|
return {
|
|
448
469
|
value: operand,
|
|
449
470
|
isValue: false,
|
|
@@ -586,9 +607,11 @@ function resolveExpression(x, c, level, contextPath, expression, rootSchema) {
|
|
|
586
607
|
return !resolvedLeft;
|
|
587
608
|
}
|
|
588
609
|
case Operator.EQUALS: {
|
|
610
|
+
// biome-ignore lint/suspicious/noDoubleEquals: Breaks prior existing functionality - see when fixture
|
|
589
611
|
return resolvedLeft == resolvedRight;
|
|
590
612
|
}
|
|
591
613
|
case Operator.NOT_EQUALS: {
|
|
614
|
+
// biome-ignore lint/suspicious/noDoubleEquals: Breaks prior existing functionality - see when fixture
|
|
592
615
|
return resolvedLeft != resolvedRight;
|
|
593
616
|
}
|
|
594
617
|
case Operator.GREATER_THAN_OR_EQUALS: {
|
|
@@ -673,13 +696,14 @@ export function transformInnerHTML(innerHTML, index = 0) {
|
|
|
673
696
|
return transformedInnerHTML;
|
|
674
697
|
}
|
|
675
698
|
/**
|
|
676
|
-
* Resolves
|
|
699
|
+
* Resolves boolean logic
|
|
700
|
+
* used for f-when and boolean attributes
|
|
677
701
|
* @param self - Where the first item in the path path refers to the item itself (used by repeat).
|
|
678
702
|
* @param chainedExpression - The chained expression which includes the expression and the next expression
|
|
679
703
|
* if there is another in the chain
|
|
680
704
|
* @returns - A binding that resolves the chained expression logic
|
|
681
705
|
*/
|
|
682
|
-
export function
|
|
706
|
+
export function getBooleanBinding(rootPropertyName, expression, parentContext, level, schema) {
|
|
683
707
|
const binding = expressionResolver(rootPropertyName, expression, parentContext, level, schema);
|
|
684
708
|
return (x, c) => binding(x, c);
|
|
685
709
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { expect, test } from "@playwright/test";
|
|
2
2
|
import { refPropertyName, Schema } from "./schema.js";
|
|
3
|
-
import {
|
|
3
|
+
import { extractPathsFromChainedExpression, findDef, getBooleanBinding, getChildrenMap, getExpressionChain, getIndexOfNextMatchingTag, getNextBehavior, pathResolver, transformInnerHTML, } from "./utilities.js";
|
|
4
4
|
test.describe("utilities", async () => {
|
|
5
5
|
test.describe("content", async () => {
|
|
6
6
|
test("get the next content binding", async () => {
|
|
@@ -17,7 +17,7 @@ test.describe("utilities", async () => {
|
|
|
17
17
|
});
|
|
18
18
|
test.describe("attributes", async () => {
|
|
19
19
|
test("get the next attribute binding", async () => {
|
|
20
|
-
const innerHTML =
|
|
20
|
+
const innerHTML = '<input type="{{type}}" disabled>';
|
|
21
21
|
const templateResult = getNextBehavior(innerHTML);
|
|
22
22
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.type).toEqual("dataBinding");
|
|
23
23
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.subtype).toEqual("attribute");
|
|
@@ -29,7 +29,7 @@ test.describe("utilities", async () => {
|
|
|
29
29
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingEndIndex).toEqual(21);
|
|
30
30
|
});
|
|
31
31
|
test("get the next attribute event binding", async () => {
|
|
32
|
-
const innerHTML =
|
|
32
|
+
const innerHTML = '<input @click="{handleClick()}">';
|
|
33
33
|
const templateResult = getNextBehavior(innerHTML);
|
|
34
34
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.type).toEqual("dataBinding");
|
|
35
35
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.subtype).toEqual("attribute");
|
|
@@ -46,10 +46,10 @@ test.describe("utilities", async () => {
|
|
|
46
46
|
expect(templateResult).toBeNull();
|
|
47
47
|
});
|
|
48
48
|
test("skip single-brace non-event, non-property attribute bindings", async () => {
|
|
49
|
-
const innerHTML1 =
|
|
49
|
+
const innerHTML1 = '<input type="{type}">';
|
|
50
50
|
const templateResult1 = getNextBehavior(innerHTML1);
|
|
51
51
|
expect(templateResult1).toBeNull();
|
|
52
|
-
const innerHTML2 =
|
|
52
|
+
const innerHTML2 = '<input ?disabled="{disabled}">';
|
|
53
53
|
const templateResult2 = getNextBehavior(innerHTML2);
|
|
54
54
|
expect(templateResult2).toBeNull();
|
|
55
55
|
});
|
|
@@ -63,7 +63,7 @@ test.describe("utilities", async () => {
|
|
|
63
63
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingStartIndex).toEqual(67);
|
|
64
64
|
});
|
|
65
65
|
test("find event binding after skipped single-brace content", async () => {
|
|
66
|
-
const innerHTML =
|
|
66
|
+
const innerHTML = '<style>.foo { color: red }</style><button @click="{handler()}">';
|
|
67
67
|
const templateResult = getNextBehavior(innerHTML);
|
|
68
68
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.type).toEqual("dataBinding");
|
|
69
69
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.subtype).toEqual("attribute");
|
|
@@ -71,7 +71,7 @@ test.describe("utilities", async () => {
|
|
|
71
71
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.bindingType).toEqual("client");
|
|
72
72
|
});
|
|
73
73
|
test("find property binding after skipped single-brace content", async () => {
|
|
74
|
-
const innerHTML =
|
|
74
|
+
const innerHTML = '<style>.foo { color: red } .bar { color: blue }</style><button :value="{someValue}">';
|
|
75
75
|
const templateResult = getNextBehavior(innerHTML);
|
|
76
76
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.type).toEqual("dataBinding");
|
|
77
77
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.subtype).toEqual("attribute");
|
|
@@ -79,13 +79,13 @@ test.describe("utilities", async () => {
|
|
|
79
79
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.bindingType).toEqual("client");
|
|
80
80
|
});
|
|
81
81
|
test("ensure if there are expected missing {} this does not cause parsing issues", async () => {
|
|
82
|
-
const innerHTML =
|
|
82
|
+
const innerHTML = '<f-when value="missing">';
|
|
83
83
|
expect(getNextBehavior(innerHTML)).toBeTruthy();
|
|
84
84
|
});
|
|
85
85
|
});
|
|
86
86
|
test.describe("templates", async () => {
|
|
87
87
|
test("when directive", async () => {
|
|
88
|
-
const innerHTML =
|
|
88
|
+
const innerHTML = '<f-when value="{{show}}">Hello world</f-when>';
|
|
89
89
|
const templateResult = getNextBehavior(innerHTML);
|
|
90
90
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.type).toEqual("templateDirective");
|
|
91
91
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.openingTagStartIndex).toEqual(0);
|
|
@@ -94,7 +94,7 @@ test.describe("utilities", async () => {
|
|
|
94
94
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingTagEndIndex).toEqual(45);
|
|
95
95
|
});
|
|
96
96
|
test("when directive with content", async () => {
|
|
97
|
-
const innerHTML =
|
|
97
|
+
const innerHTML = 'Hello pluto<f-when value="{{show}}">Hello world</f-when>';
|
|
98
98
|
const templateResult = getNextBehavior(innerHTML);
|
|
99
99
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.type).toEqual("templateDirective");
|
|
100
100
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.openingTagStartIndex).toEqual(11);
|
|
@@ -103,7 +103,7 @@ test.describe("utilities", async () => {
|
|
|
103
103
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingTagEndIndex).toEqual(56);
|
|
104
104
|
});
|
|
105
105
|
test("when directive with binding", async () => {
|
|
106
|
-
const innerHTML =
|
|
106
|
+
const innerHTML = '<f-when value="{{show}}">{{text}}</f-when>';
|
|
107
107
|
const templateResult = getNextBehavior(innerHTML);
|
|
108
108
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.type).toEqual("templateDirective");
|
|
109
109
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.openingTagStartIndex).toEqual(0);
|
|
@@ -111,10 +111,16 @@ test.describe("utilities", async () => {
|
|
|
111
111
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingTagStartIndex).toEqual(33);
|
|
112
112
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingTagEndIndex).toEqual(42);
|
|
113
113
|
});
|
|
114
|
+
test("should not treat an unknown f- tag as a directive", async () => {
|
|
115
|
+
const innerHTML = '<f-foo value="{{bar}}">Hello</f-foo>';
|
|
116
|
+
const templateResult = getNextBehavior(innerHTML);
|
|
117
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.type).toEqual("dataBinding");
|
|
118
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.subtype).toEqual("attribute");
|
|
119
|
+
});
|
|
114
120
|
});
|
|
115
121
|
test.describe("attributes", async () => {
|
|
116
122
|
test("children directive", async () => {
|
|
117
|
-
const innerHTML =
|
|
123
|
+
const innerHTML = '<ul f-children="{list}"></ul>';
|
|
118
124
|
const result = getNextBehavior(innerHTML);
|
|
119
125
|
expect(result === null || result === void 0 ? void 0 : result.type).toEqual("dataBinding");
|
|
120
126
|
expect(result === null || result === void 0 ? void 0 : result.subtype).toEqual("attributeDirective");
|
|
@@ -126,7 +132,7 @@ test.describe("utilities", async () => {
|
|
|
126
132
|
expect(result === null || result === void 0 ? void 0 : result.closingEndIndex).toEqual(22);
|
|
127
133
|
});
|
|
128
134
|
test("slotted directive", async () => {
|
|
129
|
-
const innerHTML =
|
|
135
|
+
const innerHTML = '<slot f-slotted="{slottedNodes}"></slot>';
|
|
130
136
|
const result = getNextBehavior(innerHTML);
|
|
131
137
|
expect(result === null || result === void 0 ? void 0 : result.type).toEqual("dataBinding");
|
|
132
138
|
expect(result === null || result === void 0 ? void 0 : result.subtype).toEqual("attributeDirective");
|
|
@@ -138,7 +144,7 @@ test.describe("utilities", async () => {
|
|
|
138
144
|
expect(result === null || result === void 0 ? void 0 : result.closingEndIndex).toEqual(31);
|
|
139
145
|
});
|
|
140
146
|
test("ref directive", async () => {
|
|
141
|
-
const innerHTML =
|
|
147
|
+
const innerHTML = '<video f-ref="{video}"></video>';
|
|
142
148
|
const result = getNextBehavior(innerHTML);
|
|
143
149
|
expect(result === null || result === void 0 ? void 0 : result.type).toEqual("dataBinding");
|
|
144
150
|
expect(result === null || result === void 0 ? void 0 : result.subtype).toEqual("attributeDirective");
|
|
@@ -214,7 +220,7 @@ test.describe("utilities", async () => {
|
|
|
214
220
|
leftIsValue: false,
|
|
215
221
|
right: null,
|
|
216
222
|
rightIsValue: null,
|
|
217
|
-
}
|
|
223
|
+
},
|
|
218
224
|
});
|
|
219
225
|
});
|
|
220
226
|
test("should resolve a falsy value", async () => {
|
|
@@ -225,7 +231,7 @@ test.describe("utilities", async () => {
|
|
|
225
231
|
leftIsValue: false,
|
|
226
232
|
right: null,
|
|
227
233
|
rightIsValue: null,
|
|
228
|
-
}
|
|
234
|
+
},
|
|
229
235
|
});
|
|
230
236
|
});
|
|
231
237
|
test("should resolve a path not equal to string value", async () => {
|
|
@@ -236,7 +242,7 @@ test.describe("utilities", async () => {
|
|
|
236
242
|
leftIsValue: false,
|
|
237
243
|
right: "test",
|
|
238
244
|
rightIsValue: true,
|
|
239
|
-
}
|
|
245
|
+
},
|
|
240
246
|
});
|
|
241
247
|
});
|
|
242
248
|
test("should resolve a path not equal to boolean value", async () => {
|
|
@@ -247,7 +253,7 @@ test.describe("utilities", async () => {
|
|
|
247
253
|
leftIsValue: false,
|
|
248
254
|
right: false,
|
|
249
255
|
rightIsValue: true,
|
|
250
|
-
}
|
|
256
|
+
},
|
|
251
257
|
});
|
|
252
258
|
});
|
|
253
259
|
test("should resolve a path not equal to numerical value", async () => {
|
|
@@ -258,7 +264,7 @@ test.describe("utilities", async () => {
|
|
|
258
264
|
leftIsValue: false,
|
|
259
265
|
right: 5,
|
|
260
266
|
rightIsValue: true,
|
|
261
|
-
}
|
|
267
|
+
},
|
|
262
268
|
});
|
|
263
269
|
});
|
|
264
270
|
test("should resolve chained expressions", async () => {
|
|
@@ -278,8 +284,8 @@ test.describe("utilities", async () => {
|
|
|
278
284
|
leftIsValue: false,
|
|
279
285
|
right: "baz",
|
|
280
286
|
rightIsValue: true,
|
|
281
|
-
}
|
|
282
|
-
}
|
|
287
|
+
},
|
|
288
|
+
},
|
|
283
289
|
});
|
|
284
290
|
expect(getExpressionChain("foo && bar")).toEqual({
|
|
285
291
|
expression: {
|
|
@@ -297,8 +303,8 @@ test.describe("utilities", async () => {
|
|
|
297
303
|
leftIsValue: false,
|
|
298
304
|
right: null,
|
|
299
305
|
rightIsValue: null,
|
|
300
|
-
}
|
|
301
|
-
}
|
|
306
|
+
},
|
|
307
|
+
},
|
|
302
308
|
});
|
|
303
309
|
});
|
|
304
310
|
});
|
|
@@ -401,7 +407,7 @@ test.describe("utilities", async () => {
|
|
|
401
407
|
expect(paths.has("app.user.profile.settings.theme")).toBe(true);
|
|
402
408
|
});
|
|
403
409
|
});
|
|
404
|
-
test.describe("
|
|
410
|
+
test.describe("getBooleanBinding - default case truthiness evaluation", async () => {
|
|
405
411
|
// Helper to create a basic schema for testing
|
|
406
412
|
const createTestSchema = (rootPropertyName, propertyName) => {
|
|
407
413
|
const schema = new Schema("test-element");
|
|
@@ -420,98 +426,98 @@ test.describe("utilities", async () => {
|
|
|
420
426
|
test("should evaluate boolean true as truthy", async () => {
|
|
421
427
|
const schema = createTestSchema("testData", "boolTrue");
|
|
422
428
|
const expression = getExpressionChain("boolTrue");
|
|
423
|
-
const resolver =
|
|
429
|
+
const resolver = getBooleanBinding("testData", expression, null, 0, schema);
|
|
424
430
|
const result = resolver({ boolTrue: true }, null);
|
|
425
431
|
expect(result).toBe(true);
|
|
426
432
|
});
|
|
427
433
|
test("should evaluate boolean false as falsy", async () => {
|
|
428
434
|
const schema = createTestSchema("testData", "boolFalse");
|
|
429
435
|
const expression = getExpressionChain("boolFalse");
|
|
430
|
-
const resolver =
|
|
436
|
+
const resolver = getBooleanBinding("testData", expression, null, 0, schema);
|
|
431
437
|
const result = resolver({ boolFalse: false }, null);
|
|
432
438
|
expect(result).toBe(false);
|
|
433
439
|
});
|
|
434
440
|
test("should evaluate number 0 as falsy", async () => {
|
|
435
441
|
const schema = createTestSchema("testData", "numberZero");
|
|
436
442
|
const expression = getExpressionChain("numberZero");
|
|
437
|
-
const resolver =
|
|
443
|
+
const resolver = getBooleanBinding("testData", expression, null, 0, schema);
|
|
438
444
|
const result = resolver({ numberZero: 0 }, null);
|
|
439
445
|
expect(result).toBe(false);
|
|
440
446
|
});
|
|
441
447
|
test("should evaluate positive number as truthy", async () => {
|
|
442
448
|
const schema = createTestSchema("testData", "numberPositive");
|
|
443
449
|
const expression = getExpressionChain("numberPositive");
|
|
444
|
-
const resolver =
|
|
450
|
+
const resolver = getBooleanBinding("testData", expression, null, 0, schema);
|
|
445
451
|
const result = resolver({ numberPositive: 42 }, null);
|
|
446
452
|
expect(result).toBe(true);
|
|
447
453
|
});
|
|
448
454
|
test("should evaluate negative number as truthy", async () => {
|
|
449
455
|
const schema = createTestSchema("testData", "numberNegative");
|
|
450
456
|
const expression = getExpressionChain("numberNegative");
|
|
451
|
-
const resolver =
|
|
457
|
+
const resolver = getBooleanBinding("testData", expression, null, 0, schema);
|
|
452
458
|
const result = resolver({ numberNegative: -5 }, null);
|
|
453
459
|
expect(result).toBe(true);
|
|
454
460
|
});
|
|
455
461
|
test("should evaluate empty string as falsy", async () => {
|
|
456
462
|
const schema = createTestSchema("testData", "stringEmpty");
|
|
457
463
|
const expression = getExpressionChain("stringEmpty");
|
|
458
|
-
const resolver =
|
|
464
|
+
const resolver = getBooleanBinding("testData", expression, null, 0, schema);
|
|
459
465
|
const result = resolver({ stringEmpty: "" }, null);
|
|
460
466
|
expect(result).toBe(false);
|
|
461
467
|
});
|
|
462
468
|
test("should evaluate non-empty string as truthy", async () => {
|
|
463
469
|
const schema = createTestSchema("testData", "stringNonEmpty");
|
|
464
470
|
const expression = getExpressionChain("stringNonEmpty");
|
|
465
|
-
const resolver =
|
|
471
|
+
const resolver = getBooleanBinding("testData", expression, null, 0, schema);
|
|
466
472
|
const result = resolver({ stringNonEmpty: "hello" }, null);
|
|
467
473
|
expect(result).toBe(true);
|
|
468
474
|
});
|
|
469
475
|
test("should evaluate null as falsy", async () => {
|
|
470
476
|
const schema = createTestSchema("testData", "objectNull");
|
|
471
477
|
const expression = getExpressionChain("objectNull");
|
|
472
|
-
const resolver =
|
|
478
|
+
const resolver = getBooleanBinding("testData", expression, null, 0, schema);
|
|
473
479
|
const result = resolver({ objectNull: null }, null);
|
|
474
480
|
expect(result).toBe(false);
|
|
475
481
|
});
|
|
476
482
|
test("should evaluate undefined as falsy", async () => {
|
|
477
483
|
const schema = createTestSchema("testData", "undefinedProp");
|
|
478
484
|
const expression = getExpressionChain("undefinedProp");
|
|
479
|
-
const resolver =
|
|
485
|
+
const resolver = getBooleanBinding("testData", expression, null, 0, schema);
|
|
480
486
|
const result = resolver({ undefinedProp: undefined }, null);
|
|
481
487
|
expect(result).toBe(false);
|
|
482
488
|
});
|
|
483
489
|
test("should evaluate non-null object as truthy", async () => {
|
|
484
490
|
const schema = createTestSchema("testData", "objectNonNull");
|
|
485
491
|
const expression = getExpressionChain("objectNonNull");
|
|
486
|
-
const resolver =
|
|
492
|
+
const resolver = getBooleanBinding("testData", expression, null, 0, schema);
|
|
487
493
|
const result = resolver({ objectNonNull: { foo: "bar" } }, null);
|
|
488
494
|
expect(result).toBe(true);
|
|
489
495
|
});
|
|
490
496
|
test("should evaluate empty array as truthy", async () => {
|
|
491
497
|
const schema = createTestSchema("testData", "arrayEmpty");
|
|
492
498
|
const expression = getExpressionChain("arrayEmpty");
|
|
493
|
-
const resolver =
|
|
499
|
+
const resolver = getBooleanBinding("testData", expression, null, 0, schema);
|
|
494
500
|
const result = resolver({ arrayEmpty: [] }, null);
|
|
495
501
|
expect(result).toBe(true);
|
|
496
502
|
});
|
|
497
503
|
test("should evaluate non-empty array as truthy", async () => {
|
|
498
504
|
const schema = createTestSchema("testData", "arrayNonEmpty");
|
|
499
505
|
const expression = getExpressionChain("arrayNonEmpty");
|
|
500
|
-
const resolver =
|
|
506
|
+
const resolver = getBooleanBinding("testData", expression, null, 0, schema);
|
|
501
507
|
const result = resolver({ arrayNonEmpty: [1, 2, 3] }, null);
|
|
502
508
|
expect(result).toBe(true);
|
|
503
509
|
});
|
|
504
510
|
test("should evaluate string with only whitespace as truthy", async () => {
|
|
505
511
|
const schema = createTestSchema("testData", "stringWhitespace");
|
|
506
512
|
const expression = getExpressionChain("stringWhitespace");
|
|
507
|
-
const resolver =
|
|
513
|
+
const resolver = getBooleanBinding("testData", expression, null, 0, schema);
|
|
508
514
|
const result = resolver({ stringWhitespace: " " }, null);
|
|
509
515
|
expect(result).toBe(true);
|
|
510
516
|
});
|
|
511
517
|
test("should evaluate number NaN as truthy", async () => {
|
|
512
518
|
const schema = createTestSchema("testData", "numberNaN");
|
|
513
519
|
const expression = getExpressionChain("numberNaN");
|
|
514
|
-
const resolver =
|
|
520
|
+
const resolver = getBooleanBinding("testData", expression, null, 0, schema);
|
|
515
521
|
const result = resolver({ numberNaN: NaN }, null);
|
|
516
522
|
expect(result).toBe(true);
|
|
517
523
|
});
|
|
@@ -548,37 +554,37 @@ test.describe("utilities", async () => {
|
|
|
548
554
|
test.describe("findDef", async () => {
|
|
549
555
|
test("should resolve from the root of a schema", async () => {
|
|
550
556
|
expect(findDef({
|
|
551
|
-
[refPropertyName]: "#/$defs/MyType"
|
|
557
|
+
[refPropertyName]: "#/$defs/MyType",
|
|
552
558
|
})).toEqual("MyType");
|
|
553
559
|
});
|
|
554
560
|
test("should resolve as null from an anyOf array containing a reference to another component", async () => {
|
|
555
561
|
expect(findDef({
|
|
556
562
|
anyOf: [
|
|
557
563
|
{
|
|
558
|
-
[refPropertyName]: "https://fast.design/schemas/test-element/c.json"
|
|
559
|
-
}
|
|
560
|
-
]
|
|
564
|
+
[refPropertyName]: "https://fast.design/schemas/test-element/c.json",
|
|
565
|
+
},
|
|
566
|
+
],
|
|
561
567
|
})).toEqual(null);
|
|
562
568
|
});
|
|
563
569
|
test("should resolve from an anyOf array containing a reference to a $def", async () => {
|
|
564
570
|
expect(findDef({
|
|
565
571
|
anyOf: [
|
|
566
572
|
{
|
|
567
|
-
[refPropertyName]: "#/$defs/MyType"
|
|
568
|
-
}
|
|
569
|
-
]
|
|
573
|
+
[refPropertyName]: "#/$defs/MyType",
|
|
574
|
+
},
|
|
575
|
+
],
|
|
570
576
|
})).toEqual("MyType");
|
|
571
577
|
});
|
|
572
578
|
test("should resolve from an anyOf array containing a reference to another component and a reference to a $def", async () => {
|
|
573
579
|
expect(findDef({
|
|
574
580
|
anyOf: [
|
|
575
581
|
{
|
|
576
|
-
[refPropertyName]: "https://fast.design/schemas/test-element/c.json"
|
|
582
|
+
[refPropertyName]: "https://fast.design/schemas/test-element/c.json",
|
|
577
583
|
},
|
|
578
584
|
{
|
|
579
|
-
[refPropertyName]: "#/$defs/MyType"
|
|
580
|
-
}
|
|
581
|
-
]
|
|
585
|
+
[refPropertyName]: "#/$defs/MyType",
|
|
586
|
+
},
|
|
587
|
+
],
|
|
582
588
|
})).toEqual("MyType");
|
|
583
589
|
});
|
|
584
590
|
test("should resolve as null if not found", async () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@microsoft/fast-html",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.42",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Microsoft",
|
|
@@ -23,17 +23,15 @@
|
|
|
23
23
|
"scripts": {
|
|
24
24
|
"build:tsc": "tsc -p ./tsconfig.json",
|
|
25
25
|
"build": "npm run build:tsc && npm run doc",
|
|
26
|
-
"clean": "
|
|
26
|
+
"clean": "clean dist temp test-results",
|
|
27
27
|
"dev:full": "concurrently -k -n fast-element,fast-html,server \"npm run dev --workspace=@microsoft/fast-element\" \"npm:watch\" \"npm:test-server\"",
|
|
28
28
|
"dev": "concurrently -k -n tsc,server \"npm run watch\" \"npm run test-server\"",
|
|
29
29
|
"doc:ci": "api-extractor run",
|
|
30
30
|
"doc": "npm run doc:ci -- --local",
|
|
31
|
-
"
|
|
32
|
-
"
|
|
31
|
+
"lint": "biome lint .",
|
|
32
|
+
"lint:fix": "biome lint --fix .",
|
|
33
33
|
"install-playwright-browsers": "npm run playwright install",
|
|
34
34
|
"prepublishOnly": "npm run clean && npm run build",
|
|
35
|
-
"prettier:diff": "prettier --config ../../.prettierrc \"**/*.{ts,html}\" --list-different",
|
|
36
|
-
"prettier": "prettier --config ../../.prettierrc --write \"**/*.{ts,html}\"",
|
|
37
35
|
"test-server": "npx vite test/ --clearScreen false",
|
|
38
36
|
"test:playwright": "playwright test",
|
|
39
37
|
"test:rules": "sg test --skip-snapshot-tests",
|