@microsoft/fast-html 1.0.0-alpha.2 → 1.0.0-alpha.21

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.
Files changed (52) hide show
  1. package/README.md +111 -18
  2. package/dist/dts/components/element.d.ts +10 -0
  3. package/dist/dts/components/index.d.ts +2 -0
  4. package/dist/dts/components/observer-map.d.ts +26 -0
  5. package/dist/dts/components/schema.d.ts +134 -0
  6. package/dist/dts/components/template.d.ts +39 -5
  7. package/dist/dts/components/utilities.d.ts +92 -19
  8. package/dist/dts/fixtures/observer-map/main.d.ts +1 -0
  9. package/dist/dts/fixtures/observer-map/observer-map.spec.d.ts +1 -0
  10. package/dist/dts/index.d.ts +1 -1
  11. package/dist/esm/components/element.js +27 -0
  12. package/dist/esm/components/index.js +2 -0
  13. package/dist/esm/components/observer-map.js +49 -0
  14. package/dist/esm/components/observer-map.spec.js +19 -0
  15. package/dist/esm/components/schema.js +215 -0
  16. package/dist/esm/components/schema.spec.js +257 -0
  17. package/dist/esm/components/template.js +154 -99
  18. package/dist/esm/components/utilities.js +553 -43
  19. package/dist/esm/components/utilities.spec.js +246 -44
  20. package/dist/esm/fixtures/attribute/main.js +3 -2
  21. package/dist/esm/fixtures/binding/binding.spec.js +6 -0
  22. package/dist/esm/fixtures/binding/main.js +13 -2
  23. package/dist/esm/fixtures/children/children.spec.js +4 -0
  24. package/dist/esm/fixtures/children/main.js +3 -2
  25. package/dist/esm/fixtures/dot-syntax/dot-syntax.spec.js +109 -2
  26. package/dist/esm/fixtures/dot-syntax/main.js +30 -4
  27. package/dist/esm/fixtures/event/event.spec.js +28 -5
  28. package/dist/esm/fixtures/event/main.js +21 -5
  29. package/dist/esm/fixtures/observer-map/main.js +304 -0
  30. package/dist/esm/fixtures/observer-map/observer-map.spec.js +174 -0
  31. package/dist/esm/fixtures/ref/main.js +3 -2
  32. package/dist/esm/fixtures/ref/ref.spec.js +2 -6
  33. package/dist/esm/fixtures/repeat/main.js +27 -2
  34. package/dist/esm/fixtures/repeat/repeat.spec.js +16 -6
  35. package/dist/esm/fixtures/slotted/main.js +15 -4
  36. package/dist/esm/fixtures/slotted/slotted.spec.js +18 -19
  37. package/dist/esm/fixtures/when/main.js +139 -2
  38. package/dist/esm/fixtures/when/when.spec.js +64 -1
  39. package/dist/esm/index.js +1 -1
  40. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  41. package/dist/fast-html.api.json +279 -0
  42. package/dist/fast-html.d.ts +219 -5
  43. package/dist/fast-html.untrimmed.d.ts +219 -5
  44. package/package.json +12 -9
  45. package/rules/attribute-directives.yml +38 -0
  46. package/rules/call-expression-with-event-argument.yml +41 -0
  47. package/rules/member-expression.yml +33 -0
  48. package/rules/tag-function-to-template-literal.yml +16 -0
  49. package/dist/esm/fixtures/partial/main.js +0 -31
  50. package/dist/esm/fixtures/partial/partial.spec.js +0 -14
  51. /package/dist/dts/{fixtures/partial/main.d.ts → components/observer-map.spec.d.ts} +0 -0
  52. /package/dist/dts/{fixtures/partial/partial.spec.d.ts → components/schema.spec.d.ts} +0 -0
@@ -1,30 +1,67 @@
1
1
  import { __awaiter, __decorate, __metadata } from "tslib";
2
- import { attr, FAST, FASTElement, fastElementRegistry, ViewTemplate, } from "@microsoft/fast-element";
3
- import { DOMPolicy } from "@microsoft/fast-element/dom-policy.js";
4
- import { getAllPartials, getNextBehavior, pathResolver, } from "./utilities.js";
2
+ import { attr, elements, FAST, FASTElement, FASTElementDefinition, fastElementRegistry, HydratableElementController, ViewTemplate, } from "@microsoft/fast-element";
3
+ import "@microsoft/fast-element/install-hydratable-view-templates.js";
4
+ import { bindingResolver, getExpressionChain, getNextBehavior, getRootPropertyName, resolveWhen, transformInnerHTML, } from "./utilities.js";
5
+ import { ObserverMap } from "./observer-map.js";
6
+ import { Schema } from "./schema.js";
5
7
  /**
6
8
  * The <f-template> custom element that will provide view logic to the element
7
9
  */
8
10
  class TemplateElement extends FASTElement {
11
+ static options(elementOptions = {}) {
12
+ const result = {};
13
+ for (const key in elementOptions) {
14
+ const value = elementOptions[key];
15
+ result[key] = {
16
+ observerMap: value.observerMap,
17
+ };
18
+ }
19
+ TemplateElement.elementOptions = result;
20
+ HydratableElementController.install();
21
+ return this;
22
+ }
9
23
  constructor() {
10
- super(...arguments);
24
+ super();
11
25
  this.partials = {};
26
+ // Ensure elementOptions is initialized if it's empty
27
+ if (!TemplateElement.elementOptions ||
28
+ Object.keys(TemplateElement.elementOptions).length === 0) {
29
+ TemplateElement.options();
30
+ }
31
+ }
32
+ /**
33
+ * Set options for a custom element
34
+ * @param name - The name of the custom element to set options for.
35
+ */
36
+ static setOptions(name) {
37
+ if (!TemplateElement.elementOptions[name]) {
38
+ TemplateElement.elementOptions[name] = TemplateElement.defaultElementOptions;
39
+ }
12
40
  }
13
41
  connectedCallback() {
14
42
  super.connectedCallback();
15
- if (this.name) {
16
- this.$fastController.definition.registry
17
- .whenDefined(this.name)
18
- .then((value) => __awaiter(this, void 0, void 0, function* () {
43
+ if (typeof this.name === "string") {
44
+ this.schema = new Schema(this.name);
45
+ FASTElementDefinition.registerAsync(this.name).then((value) => __awaiter(this, void 0, void 0, function* () {
46
+ var _a, _b, _c;
47
+ if (!((_a = TemplateElement.elementOptions) === null || _a === void 0 ? void 0 : _a[this.name])) {
48
+ TemplateElement.setOptions(this.name);
49
+ }
50
+ if (((_b = TemplateElement.elementOptions[this.name]) === null || _b === void 0 ? void 0 : _b.observerMap) ===
51
+ "all") {
52
+ this.observerMap = new ObserverMap(value.prototype, this.schema);
53
+ }
19
54
  const registeredFastElement = fastElementRegistry.getByType(value);
20
55
  const template = this.getElementsByTagName("template").item(0);
21
56
  if (template) {
22
- yield this.resolveAllPartials(this.innerHTML);
23
- const { strings, values } = yield this.resolveStringsAndValues(this.innerHTML);
57
+ const innerHTML = yield transformInnerHTML(this.innerHTML);
58
+ // Cache paths during template processing (pass undefined if observerMap is not available)
59
+ const { strings, values } = yield this.resolveStringsAndValues(null, innerHTML, false, null, 0, this.schema, this.observerMap);
60
+ // Define the root properties cached in the observer map as observable (only if observerMap exists)
61
+ (_c = this.observerMap) === null || _c === void 0 ? void 0 : _c.defineProperties();
24
62
  if (registeredFastElement) {
25
63
  // all new elements will get the updated template
26
- registeredFastElement.template =
27
- this.resolveTemplateOrBehavior(strings, values);
64
+ registeredFastElement.template = this.resolveTemplateOrBehavior(strings, values);
28
65
  }
29
66
  }
30
67
  else {
@@ -37,12 +74,13 @@ class TemplateElement extends FASTElement {
37
74
  * Resolve strings and values from an innerHTML string
38
75
  * @param innerHTML - The innerHTML.
39
76
  * @param self - Indicates that this should refer to itself instead of a property when creating bindings.
77
+ * @param observerMap - ObserverMap instance for caching binding paths (optional).
40
78
  */
41
- resolveStringsAndValues(innerHTML, self = false) {
79
+ resolveStringsAndValues(rootPropertyName, innerHTML, self = false, parentContext, level, schema, observerMap) {
42
80
  return __awaiter(this, void 0, void 0, function* () {
43
81
  const strings = [];
44
82
  const values = []; // these can be bindings, directives, etc.
45
- yield this.resolveInnerHTML(innerHTML, strings, values, self);
83
+ yield this.resolveInnerHTML(rootPropertyName, innerHTML, strings, values, self, parentContext, level, schema, observerMap);
46
84
  strings.raw = strings.map(value => String.raw({ raw: value }));
47
85
  return {
48
86
  strings,
@@ -56,44 +94,36 @@ class TemplateElement extends FASTElement {
56
94
  * @param values - The interpreted values.
57
95
  */
58
96
  resolveTemplateOrBehavior(strings, values) {
59
- return ViewTemplate.create(strings, values, DOMPolicy.create());
97
+ return ViewTemplate.create(strings, values);
60
98
  }
61
99
  /**
62
100
  * Resolve a template directive
63
101
  * @param behaviorConfig - The directive behavior configuration object.
64
102
  * @param externalValues - The interpreted values from the parent.
65
103
  * @param innerHTML - The innerHTML.
104
+ * @param self - Indicates that this should refer to itself instead of a property when creating bindings.
105
+ * @param observerMap - ObserverMap instance for caching binding paths (optional).
66
106
  */
67
- resolveTemplateDirective(behaviorConfig, externalValues, innerHTML, self = false) {
68
- var _a;
107
+ resolveTemplateDirective(rootPropertyName, behaviorConfig, externalValues, innerHTML, self = false, parentContext, level, schema, observerMap) {
69
108
  return __awaiter(this, void 0, void 0, function* () {
70
109
  switch (behaviorConfig.name) {
71
- case "when":
72
- {
73
- const { strings, values } = yield this.resolveStringsAndValues(innerHTML.slice(behaviorConfig.openingTagEndIndex, behaviorConfig.closingTagStartIndex));
74
- const { when } = yield import("@microsoft/fast-element");
75
- externalValues.push(when(x => pathResolver(behaviorConfig.value, self)(x), this.resolveTemplateOrBehavior(strings, values)));
76
- }
110
+ case "when": {
111
+ const { when } = yield import("@microsoft/fast-element");
112
+ const expressionChain = getExpressionChain(behaviorConfig.value);
113
+ const whenLogic = resolveWhen(rootPropertyName, expressionChain, parentContext, level, schema);
114
+ const { strings, values } = yield this.resolveStringsAndValues(rootPropertyName, innerHTML.slice(behaviorConfig.openingTagEndIndex, behaviorConfig.closingTagStartIndex), self, parentContext, level, schema, observerMap);
115
+ externalValues.push(when(whenLogic, this.resolveTemplateOrBehavior(strings, values)));
77
116
  break;
78
- case "repeat":
79
- {
80
- const valueAttr = behaviorConfig.value.split(" "); // syntax {{x in y}}
81
- const { strings, values } = yield this.resolveStringsAndValues(innerHTML.slice(behaviorConfig.openingTagEndIndex, behaviorConfig.closingTagStartIndex), true);
82
- const { repeat } = yield import("@microsoft/fast-element");
83
- externalValues.push(repeat(x => pathResolver(valueAttr[2], self)(x), this.resolveTemplateOrBehavior(strings, values)));
84
- }
117
+ }
118
+ case "repeat": {
119
+ const valueAttr = behaviorConfig.value.split(" "); // syntax {{x in y}}
120
+ const updatedLevel = level + 1;
121
+ const { repeat } = yield import("@microsoft/fast-element");
122
+ rootPropertyName = getRootPropertyName(rootPropertyName, valueAttr[2], parentContext, behaviorConfig.name);
123
+ const binding = bindingResolver(rootPropertyName, valueAttr[2], parentContext, behaviorConfig.name, schema, valueAttr[0], level);
124
+ const { strings, values } = yield this.resolveStringsAndValues(rootPropertyName, innerHTML.slice(behaviorConfig.openingTagEndIndex, behaviorConfig.closingTagStartIndex), true, valueAttr[0], updatedLevel, schema, observerMap);
125
+ externalValues.push(repeat((x, c) => binding(x, c), this.resolveTemplateOrBehavior(strings, values)));
85
126
  break;
86
- case "apply": {
87
- const openingTag = innerHTML.slice(behaviorConfig.openingTagStartIndex, behaviorConfig.openingTagEndIndex);
88
- const partial = (_a = openingTag
89
- .split(" ")
90
- .find(tagPart => {
91
- return tagPart.startsWith("partial");
92
- })) === null || _a === void 0 ? void 0 : _a.split('"')[1];
93
- if (partial) {
94
- const { when } = yield import("@microsoft/fast-element");
95
- externalValues.push(when(x => pathResolver(behaviorConfig.value, self)(x), () => this.partials[partial]));
96
- }
97
127
  }
98
128
  }
99
129
  });
@@ -107,24 +137,34 @@ class TemplateElement extends FASTElement {
107
137
  resolveAttributeDirective(name, propName, externalValues) {
108
138
  return __awaiter(this, void 0, void 0, function* () {
109
139
  switch (name) {
110
- case "children":
111
- {
112
- const { children } = yield import("@microsoft/fast-element");
113
- externalValues.push(children(propName));
114
- }
140
+ case "children": {
141
+ const { children } = yield import("@microsoft/fast-element");
142
+ externalValues.push(children(propName));
115
143
  break;
116
- case "slotted":
117
- {
118
- const { slotted } = yield import("@microsoft/fast-element");
119
- externalValues.push(slotted(propName));
144
+ }
145
+ case "slotted": {
146
+ const { slotted } = yield import("@microsoft/fast-element");
147
+ const parts = propName.trim().split(" filter ");
148
+ const slottedOption = {
149
+ property: parts[0],
150
+ };
151
+ if (parts[1]) {
152
+ if (parts[1].startsWith("elements(")) {
153
+ let params = parts[1].replace("elements(", "");
154
+ params = params.substring(0, params.lastIndexOf(")"));
155
+ Object.assign(slottedOption, {
156
+ filter: elements(params || undefined),
157
+ });
158
+ }
120
159
  }
160
+ externalValues.push(slotted(slottedOption));
121
161
  break;
122
- case "ref":
123
- {
124
- const { ref } = yield import("@microsoft/fast-element");
125
- externalValues.push(ref(propName));
126
- }
162
+ }
163
+ case "ref": {
164
+ const { ref } = yield import("@microsoft/fast-element");
165
+ externalValues.push(ref(propName));
127
166
  break;
167
+ }
128
168
  }
129
169
  });
130
170
  }
@@ -135,43 +175,60 @@ class TemplateElement extends FASTElement {
135
175
  * @param values - The interpreted values.
136
176
  * @param self - Indicates that this should refer to itself instead of a property when creating bindings.
137
177
  * @param behaviorConfig - The binding behavior configuration object.
178
+ * @param observerMap - ObserverMap instance for caching binding paths (optional).
138
179
  */
139
- resolveDataBinding(innerHTML, strings, values, self = false, behaviorConfig) {
180
+ resolveDataBinding(rootPropertyName, innerHTML, strings, values, self = false, behaviorConfig, parentContext, level, schema, observerMap) {
140
181
  return __awaiter(this, void 0, void 0, function* () {
141
182
  switch (behaviorConfig.subtype) {
142
- case "content":
143
- {
144
- strings.push(innerHTML.slice(0, behaviorConfig.openingStartIndex));
145
- const propName = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex);
146
- const binding = (x) => pathResolver(propName, self)(x);
147
- values.push(binding);
148
- yield this.resolveInnerHTML(innerHTML.slice(behaviorConfig.closingEndIndex, innerHTML.length), strings, values, self);
149
- }
183
+ case "content": {
184
+ strings.push(innerHTML.slice(0, behaviorConfig.openingStartIndex));
185
+ const type = "access";
186
+ const propName = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex);
187
+ rootPropertyName = getRootPropertyName(rootPropertyName, propName, parentContext, type);
188
+ const binding = bindingResolver(rootPropertyName, propName, parentContext, type, schema, parentContext, level);
189
+ const contentBinding = (x, c) => binding(x, c);
190
+ values.push(contentBinding);
191
+ yield this.resolveInnerHTML(rootPropertyName, innerHTML.slice(behaviorConfig.closingEndIndex, innerHTML.length), strings, values, self, parentContext, level, schema, observerMap);
150
192
  break;
151
- case "attribute":
193
+ }
194
+ case "attribute": {
152
195
  strings.push(innerHTML.slice(0, behaviorConfig.openingStartIndex));
153
196
  if (behaviorConfig.aspect === "@") {
154
- const propName = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex - 2);
155
- const binding = (x) => pathResolver(propName, self)(x)();
156
- values.push(binding);
197
+ const bindingHTML = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex);
198
+ const openingParenthesis = bindingHTML.indexOf("(");
199
+ const closingParenthesis = bindingHTML.indexOf(")");
200
+ const propName = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex -
201
+ (closingParenthesis - openingParenthesis) -
202
+ 1);
203
+ const type = "event";
204
+ rootPropertyName = getRootPropertyName(rootPropertyName, propName, parentContext, type);
205
+ const arg = bindingHTML.slice(openingParenthesis + 1, closingParenthesis);
206
+ const binding = bindingResolver(rootPropertyName, propName, parentContext, type, schema, parentContext, level);
207
+ const attributeBinding = (x, c) => binding(x, c).bind(x)(...(arg === "e" ? [c.event] : []), ...(arg !== "e" && arg !== ""
208
+ ? [
209
+ bindingResolver(rootPropertyName, arg, parentContext, type, schema, parentContext, level)(x, c),
210
+ ]
211
+ : []));
212
+ values.push(attributeBinding);
157
213
  }
158
214
  else {
159
215
  const propName = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex);
160
- const binding = (x) => pathResolver(propName, self)(x);
161
- values.push(binding);
216
+ const type = "access";
217
+ rootPropertyName = getRootPropertyName(rootPropertyName, propName, parentContext, type);
218
+ const binding = bindingResolver(rootPropertyName, propName, parentContext, type, schema, parentContext, level);
219
+ const attributeBinding = (x, c) => binding(x, c);
220
+ values.push(attributeBinding);
162
221
  }
163
- yield this.resolveInnerHTML(innerHTML.slice(behaviorConfig.closingEndIndex, innerHTML.length), strings, values, self);
222
+ yield this.resolveInnerHTML(rootPropertyName, innerHTML.slice(behaviorConfig.closingEndIndex, innerHTML.length), strings, values, self, parentContext, level, schema, observerMap);
164
223
  break;
165
- case "attributeDirective":
166
- {
167
- strings.push(innerHTML.slice(0, behaviorConfig.openingStartIndex -
168
- behaviorConfig.name.length -
169
- 4));
170
- const propName = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex);
171
- yield this.resolveAttributeDirective(behaviorConfig.name, propName, values);
172
- yield this.resolveInnerHTML(innerHTML.slice(behaviorConfig.closingEndIndex + 1, innerHTML.length), strings, values, self);
173
- }
224
+ }
225
+ case "attributeDirective": {
226
+ strings.push(innerHTML.slice(0, behaviorConfig.openingStartIndex - behaviorConfig.name.length - 4));
227
+ const propName = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex);
228
+ yield this.resolveAttributeDirective(behaviorConfig.name, propName, values);
229
+ yield this.resolveInnerHTML(rootPropertyName, innerHTML.slice(behaviorConfig.closingEndIndex + 1, innerHTML.length), strings, values, self, parentContext, level, schema, observerMap);
174
230
  break;
231
+ }
175
232
  }
176
233
  });
177
234
  }
@@ -181,8 +238,9 @@ class TemplateElement extends FASTElement {
181
238
  * @param strings - The strings array.
182
239
  * @param values - The interpreted values.
183
240
  * @param self - Indicates that this should refer to itself instead of a property when creating bindings.
241
+ * @param observerMap - ObserverMap instance for caching binding paths (optional).
184
242
  */
185
- resolveInnerHTML(innerHTML, strings, values, self = false) {
243
+ resolveInnerHTML(rootPropertyName, innerHTML, strings, values, self = false, parentContext, level, schema, observerMap) {
186
244
  return __awaiter(this, void 0, void 0, function* () {
187
245
  const behaviorConfig = getNextBehavior(innerHTML);
188
246
  if (behaviorConfig === null) {
@@ -190,32 +248,29 @@ class TemplateElement extends FASTElement {
190
248
  }
191
249
  else {
192
250
  switch (behaviorConfig.type) {
193
- case "dataBinding":
194
- yield this.resolveDataBinding(innerHTML, strings, values, self, behaviorConfig);
251
+ case "dataBinding": {
252
+ yield this.resolveDataBinding(rootPropertyName, innerHTML, strings, values, self, behaviorConfig, parentContext, level, schema, observerMap);
195
253
  break;
196
- case "templateDirective":
254
+ }
255
+ case "templateDirective": {
197
256
  strings.push(innerHTML.slice(0, behaviorConfig.openingTagStartIndex));
198
- yield this.resolveTemplateDirective(behaviorConfig, values, innerHTML, self);
199
- yield this.resolveInnerHTML(innerHTML.slice(behaviorConfig.closingTagEndIndex, innerHTML.length), strings, values, self);
257
+ yield this.resolveTemplateDirective(rootPropertyName, behaviorConfig, values, innerHTML, self, parentContext, level, schema, observerMap);
258
+ yield this.resolveInnerHTML(rootPropertyName, innerHTML.slice(behaviorConfig.closingTagEndIndex, innerHTML.length), strings, values, self, parentContext, level, schema, observerMap);
200
259
  break;
260
+ }
201
261
  }
202
262
  }
203
263
  });
204
264
  }
205
- /**
206
- * Resolve all partial templates
207
- * @param unresolvedInnerHTML - The innerHTML.
208
- */
209
- resolveAllPartials(unresolvedInnerHTML) {
210
- return __awaiter(this, void 0, void 0, function* () {
211
- const allPartials = Object.entries(getAllPartials(unresolvedInnerHTML));
212
- for (let i = 0, partialLength = allPartials.length; i < partialLength; i++) {
213
- const { strings, values } = yield this.resolveStringsAndValues(allPartials[i][1].innerHTML);
214
- this.partials[allPartials[i][0]] = this.resolveTemplateOrBehavior(strings, values);
215
- }
216
- });
217
- }
218
265
  }
266
+ /**
267
+ * A dictionary of custom element options
268
+ */
269
+ TemplateElement.elementOptions = {};
270
+ /**
271
+ * Default element options
272
+ */
273
+ TemplateElement.defaultElementOptions = {};
219
274
  __decorate([
220
275
  attr,
221
276
  __metadata("design:type", String)