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