@microsoft/fast-html 1.0.0-alpha.39 → 1.0.0-alpha.40
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.
|
@@ -72,7 +72,7 @@ export declare function getIndexOfNextMatchingTag(openingTagStartSlice: string,
|
|
|
72
72
|
* @param innerHTML - The innerHTML string to evaluate
|
|
73
73
|
* @returns DataBindingBehaviorConfig | DirectiveBehaviorConfig | null - A configuration object or null
|
|
74
74
|
*/
|
|
75
|
-
export declare function getNextBehavior(innerHTML: string): DataBindingBehaviorConfig | TemplateDirectiveBehaviorConfig | null;
|
|
75
|
+
export declare function getNextBehavior(innerHTML: string, offset?: number): DataBindingBehaviorConfig | TemplateDirectiveBehaviorConfig | null;
|
|
76
76
|
/**
|
|
77
77
|
* Create a function to resolve a value from an object using a path with dot syntax.
|
|
78
78
|
* e.g. "foo.bar"
|
|
@@ -246,16 +246,67 @@ function getNextDataBindingBehavior(innerHTML) {
|
|
|
246
246
|
* @param innerHTML - The innerHTML string to evaluate
|
|
247
247
|
* @returns DataBindingBehaviorConfig | DirectiveBehaviorConfig | null - A configuration object or null
|
|
248
248
|
*/
|
|
249
|
-
export function getNextBehavior(innerHTML) {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
249
|
+
export function getNextBehavior(innerHTML, offset = 0) {
|
|
250
|
+
// eslint-disable-next-line no-constant-condition
|
|
251
|
+
while (true) {
|
|
252
|
+
const currentSlice = innerHTML.slice(offset);
|
|
253
|
+
// client side binding will capture all bindings starting with "{"
|
|
254
|
+
const dataBindingOpen = currentSlice.indexOf(openClientSideBinding);
|
|
255
|
+
const directiveBindingOpen = currentSlice.indexOf(openTagStart);
|
|
256
|
+
const nextDataBindingBehavior = getNextDataBindingBehavior(currentSlice);
|
|
257
|
+
if (dataBindingOpen === -1 && directiveBindingOpen === -1) {
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
if (dataBindingOpen !== -1 &&
|
|
261
|
+
nextDataBindingBehavior.bindingType === "client" &&
|
|
262
|
+
!isLegitimateClientSideBinding(nextDataBindingBehavior)) {
|
|
263
|
+
offset = nextDataBindingBehavior.closingEndIndex + offset;
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
if (directiveBindingOpen !== -1 &&
|
|
267
|
+
(dataBindingOpen === -1 || dataBindingOpen > directiveBindingOpen)) {
|
|
268
|
+
return offsetDirective(getNextDirectiveBehavior(currentSlice), offset);
|
|
269
|
+
}
|
|
270
|
+
return offsetDataBinding(nextDataBindingBehavior, offset);
|
|
257
271
|
}
|
|
258
|
-
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Apply an offset to a data binding
|
|
275
|
+
* @param config DataBindingBehaviorConfig
|
|
276
|
+
* @param offset number
|
|
277
|
+
* @returns DataBindingBehaviorConfig
|
|
278
|
+
*/
|
|
279
|
+
function offsetDataBinding(config, offset) {
|
|
280
|
+
config.openingStartIndex = config.openingStartIndex + offset;
|
|
281
|
+
config.openingEndIndex = config.openingEndIndex + offset;
|
|
282
|
+
config.closingStartIndex = config.closingStartIndex + offset;
|
|
283
|
+
config.closingEndIndex = config.closingEndIndex + offset;
|
|
284
|
+
return config;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Apply an offset to a directive
|
|
288
|
+
* @param config TemplateDirectiveBehaviorConfig
|
|
289
|
+
* @param offset number
|
|
290
|
+
* @returns TemplateDirectiveBehaviorConfig
|
|
291
|
+
*/
|
|
292
|
+
function offsetDirective(config, offset) {
|
|
293
|
+
config.openingTagStartIndex = config.openingTagStartIndex + offset;
|
|
294
|
+
config.openingTagEndIndex = config.openingTagEndIndex + offset;
|
|
295
|
+
config.closingTagStartIndex = config.closingTagStartIndex + offset;
|
|
296
|
+
config.closingTagEndIndex = config.closingTagEndIndex + offset;
|
|
297
|
+
return config;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Determine if this client side binding is legitimate.
|
|
301
|
+
* Single-brace (client) bindings are only valid for events, properties, and attribute directives.
|
|
302
|
+
* Checking for this prevents CSS/JS curly braces from being misinterpreted as bindings.
|
|
303
|
+
* @param result
|
|
304
|
+
* @returns
|
|
305
|
+
*/
|
|
306
|
+
function isLegitimateClientSideBinding(result) {
|
|
307
|
+
return ((result.subtype === "attribute" &&
|
|
308
|
+
(result.aspect === "@" || result.aspect === ":")) ||
|
|
309
|
+
result.subtype === "attributeDirective");
|
|
259
310
|
}
|
|
260
311
|
/**
|
|
261
312
|
* Create a function to resolve a value from an object using a path with dot syntax.
|
|
@@ -40,6 +40,48 @@ test.describe("utilities", async () => {
|
|
|
40
40
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingStartIndex).toEqual(29);
|
|
41
41
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingEndIndex).toEqual(30);
|
|
42
42
|
});
|
|
43
|
+
test("skip single-brace content bindings (e.g. CSS braces)", async () => {
|
|
44
|
+
const innerHTML = "<style>.foo { color: red }</style>";
|
|
45
|
+
const templateResult = getNextBehavior(innerHTML);
|
|
46
|
+
expect(templateResult).toBeNull();
|
|
47
|
+
});
|
|
48
|
+
test("skip single-brace non-event, non-property attribute bindings", async () => {
|
|
49
|
+
const innerHTML1 = "<input type=\"{type}\">";
|
|
50
|
+
const templateResult1 = getNextBehavior(innerHTML1);
|
|
51
|
+
expect(templateResult1).toBeNull();
|
|
52
|
+
const innerHTML2 = "<input ?disabled=\"{disabled}\">";
|
|
53
|
+
const templateResult2 = getNextBehavior(innerHTML2);
|
|
54
|
+
expect(templateResult2).toBeNull();
|
|
55
|
+
});
|
|
56
|
+
test("find double-brace binding after skipped single-brace content", async () => {
|
|
57
|
+
const innerHTML = "<style>.foo { color: red } .bar { color: blue }</style><span>{{name}}</span>";
|
|
58
|
+
const templateResult = getNextBehavior(innerHTML);
|
|
59
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.type).toEqual("dataBinding");
|
|
60
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.subtype).toEqual("content");
|
|
61
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.bindingType).toEqual("default");
|
|
62
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.openingStartIndex).toEqual(61);
|
|
63
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingStartIndex).toEqual(67);
|
|
64
|
+
});
|
|
65
|
+
test("find event binding after skipped single-brace content", async () => {
|
|
66
|
+
const innerHTML = "<style>.foo { color: red }</style><button @click=\"{handler()}\">";
|
|
67
|
+
const templateResult = getNextBehavior(innerHTML);
|
|
68
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.type).toEqual("dataBinding");
|
|
69
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.subtype).toEqual("attribute");
|
|
70
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.aspect).toEqual("@");
|
|
71
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.bindingType).toEqual("client");
|
|
72
|
+
});
|
|
73
|
+
test("find property binding after skipped single-brace content", async () => {
|
|
74
|
+
const innerHTML = "<style>.foo { color: red } .bar { color: blue }</style><button :value=\"{someValue}\">";
|
|
75
|
+
const templateResult = getNextBehavior(innerHTML);
|
|
76
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.type).toEqual("dataBinding");
|
|
77
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.subtype).toEqual("attribute");
|
|
78
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.aspect).toEqual(":");
|
|
79
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.bindingType).toEqual("client");
|
|
80
|
+
});
|
|
81
|
+
test("ensure if there are expected missing {} this does not cause parsing issues", async () => {
|
|
82
|
+
const innerHTML = "<f-when value=\"missing\">";
|
|
83
|
+
expect(getNextBehavior(innerHTML)).toBeTruthy();
|
|
84
|
+
});
|
|
43
85
|
});
|
|
44
86
|
test.describe("templates", async () => {
|
|
45
87
|
test("when directive", async () => {
|
package/dist/fast-html.api.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"metadata": {
|
|
3
3
|
"toolPackage": "@microsoft/api-extractor",
|
|
4
|
-
"toolVersion": "7.
|
|
4
|
+
"toolVersion": "7.57.7",
|
|
5
5
|
"schemaVersion": 1011,
|
|
6
6
|
"oldestForwardsCompatibleVersion": 1001,
|
|
7
7
|
"tsdocConfig": {
|
|
@@ -114,6 +114,22 @@
|
|
|
114
114
|
"tagName": "@virtual",
|
|
115
115
|
"syntaxKind": "modifier"
|
|
116
116
|
},
|
|
117
|
+
{
|
|
118
|
+
"tagName": "@jsx",
|
|
119
|
+
"syntaxKind": "block"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"tagName": "@jsxRuntime",
|
|
123
|
+
"syntaxKind": "block"
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"tagName": "@jsxFrag",
|
|
127
|
+
"syntaxKind": "block"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"tagName": "@jsxImportSource",
|
|
131
|
+
"syntaxKind": "block"
|
|
132
|
+
},
|
|
117
133
|
{
|
|
118
134
|
"tagName": "@betaDocumentation",
|
|
119
135
|
"syntaxKind": "modifier"
|