@microsoft/fast-element 2.0.0-beta.8 → 2.0.0

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 (142) hide show
  1. package/.eslintrc.json +1 -1
  2. package/CHANGELOG.json +512 -0
  3. package/CHANGELOG.md +180 -1
  4. package/README.md +1 -9
  5. package/api-extractor.context.json +14 -0
  6. package/api-extractor.di.json +14 -0
  7. package/dist/context/context.api.json +1068 -0
  8. package/dist/di/di.api.json +4929 -0
  9. package/dist/dts/binding/binding.d.ts +49 -0
  10. package/dist/dts/binding/normalize.d.ts +9 -0
  11. package/dist/dts/binding/one-time.d.ts +11 -0
  12. package/dist/dts/binding/one-way.d.ts +20 -0
  13. package/dist/dts/{templating/binding-signal.d.ts → binding/signal.d.ts} +19 -4
  14. package/dist/dts/{templating/binding-two-way.d.ts → binding/two-way.d.ts} +9 -5
  15. package/dist/dts/components/attributes.d.ts +6 -0
  16. package/dist/dts/components/element-controller.d.ts +104 -8
  17. package/dist/dts/components/element-hydration.d.ts +2 -0
  18. package/dist/dts/components/fast-definitions.d.ts +6 -0
  19. package/dist/dts/components/hydration.d.ts +56 -0
  20. package/dist/dts/components/install-hydration.d.ts +1 -0
  21. package/dist/dts/context.d.ts +29 -15
  22. package/dist/dts/di/di.d.ts +0 -5
  23. package/dist/dts/dom-policy.d.ts +83 -0
  24. package/dist/dts/dom.d.ts +100 -0
  25. package/dist/dts/hydration/target-builder.d.ts +63 -0
  26. package/dist/dts/index.d.ts +33 -26
  27. package/dist/dts/index.rollup.d.ts +0 -1
  28. package/dist/dts/index.rollup.debug.d.ts +0 -1
  29. package/dist/dts/interfaces.d.ts +32 -82
  30. package/dist/dts/metadata.d.ts +6 -5
  31. package/dist/dts/observation/arrays.d.ts +1 -1
  32. package/dist/dts/observation/observable.bench.d.ts +18 -0
  33. package/dist/dts/observation/observable.d.ts +5 -5
  34. package/dist/dts/pending-task.d.ts +19 -7
  35. package/dist/dts/platform.d.ts +11 -2
  36. package/dist/dts/polyfills.d.ts +0 -8
  37. package/dist/dts/styles/css-binding-directive.d.ts +60 -0
  38. package/dist/dts/styles/css.d.ts +9 -7
  39. package/dist/dts/styles/element-styles.d.ts +1 -14
  40. package/dist/dts/styles/host.d.ts +2 -5
  41. package/dist/dts/styles/style-strategy.d.ts +42 -0
  42. package/dist/dts/templating/compiler.d.ts +11 -13
  43. package/dist/dts/templating/{binding.d.ts → html-binding-directive.d.ts} +22 -42
  44. package/dist/dts/templating/html-directive.d.ts +44 -140
  45. package/dist/dts/templating/install-hydratable-view-templates.d.ts +1 -0
  46. package/dist/dts/templating/node-observation.d.ts +11 -1
  47. package/dist/dts/templating/ref.d.ts +4 -0
  48. package/dist/dts/templating/render.bench.d.ts +3 -0
  49. package/dist/dts/templating/render.d.ts +49 -9
  50. package/dist/dts/templating/repeat-basic-reverse.bench.d.ts +3 -0
  51. package/dist/dts/templating/repeat-basic-shift.bench.d.ts +3 -0
  52. package/dist/dts/templating/repeat.d.ts +31 -9
  53. package/dist/dts/templating/template.d.ts +97 -12
  54. package/dist/dts/templating/view.d.ts +149 -26
  55. package/dist/dts/templating/when-basic.bench.d.ts +3 -0
  56. package/dist/dts/templating/when-conditional.bench.d.ts +3 -0
  57. package/dist/dts/templating/when-switch.bench.d.ts +3 -0
  58. package/dist/dts/templating/when.d.ts +3 -1
  59. package/dist/dts/testing/fakes.d.ts +12 -1
  60. package/dist/dts/tsdoc-metadata.json +1 -1
  61. package/dist/dts/utilities.d.ts +55 -1
  62. package/dist/esm/binding/binding.js +18 -0
  63. package/dist/esm/binding/normalize.js +17 -0
  64. package/dist/esm/binding/one-time.js +21 -0
  65. package/dist/esm/binding/one-way.js +30 -0
  66. package/dist/esm/{templating/binding-signal.js → binding/signal.js} +22 -6
  67. package/dist/esm/{templating/binding-two-way.js → binding/two-way.js} +18 -12
  68. package/dist/esm/components/attributes.js +16 -1
  69. package/dist/esm/components/element-controller.js +319 -49
  70. package/dist/esm/components/element-hydration.js +2 -0
  71. package/dist/esm/components/fast-definitions.js +12 -4
  72. package/dist/esm/components/fast-element.js +3 -1
  73. package/dist/esm/components/hydration.js +104 -0
  74. package/dist/esm/components/install-hydration.js +3 -0
  75. package/dist/esm/context.js +26 -4
  76. package/dist/esm/debug.js +8 -2
  77. package/dist/esm/di/di.js +9 -12
  78. package/dist/esm/dom-policy.js +345 -0
  79. package/dist/esm/dom.js +101 -0
  80. package/dist/esm/hydration/target-builder.js +175 -0
  81. package/dist/esm/index.js +34 -25
  82. package/dist/esm/index.rollup.debug.js +3 -1
  83. package/dist/esm/index.rollup.js +3 -1
  84. package/dist/esm/interfaces.js +51 -3
  85. package/dist/esm/metadata.js +11 -8
  86. package/dist/esm/observation/arrays.js +1 -1
  87. package/dist/esm/observation/observable.bench.js +79 -0
  88. package/dist/esm/observation/observable.js +20 -15
  89. package/dist/esm/observation/update-queue.js +2 -2
  90. package/dist/esm/pending-task.js +13 -1
  91. package/dist/esm/platform.js +12 -2
  92. package/dist/esm/polyfills.js +3 -61
  93. package/dist/esm/styles/css-binding-directive.js +76 -0
  94. package/dist/esm/styles/css.js +14 -7
  95. package/dist/esm/styles/element-styles.js +0 -33
  96. package/dist/esm/styles/style-strategy.js +1 -0
  97. package/dist/esm/templating/children.js +8 -4
  98. package/dist/esm/templating/compiler.js +37 -44
  99. package/dist/esm/templating/html-binding-directive.js +218 -0
  100. package/dist/esm/templating/html-directive.js +25 -152
  101. package/dist/esm/templating/install-hydratable-view-templates.js +17 -0
  102. package/dist/esm/templating/node-observation.js +14 -8
  103. package/dist/esm/templating/ref.js +1 -1
  104. package/dist/esm/templating/render.bench.js +56 -0
  105. package/dist/esm/templating/render.js +74 -30
  106. package/dist/esm/templating/repeat-basic-reverse.bench.js +43 -0
  107. package/dist/esm/templating/repeat-basic-shift.bench.js +43 -0
  108. package/dist/esm/templating/repeat.js +116 -17
  109. package/dist/esm/templating/template.js +135 -60
  110. package/dist/esm/templating/view.js +259 -32
  111. package/dist/esm/templating/when-basic.bench.js +36 -0
  112. package/dist/esm/templating/when-conditional.bench.js +39 -0
  113. package/dist/esm/templating/when-switch.bench.js +68 -0
  114. package/dist/esm/templating/when.js +12 -5
  115. package/dist/esm/testing/fakes.js +32 -1
  116. package/dist/esm/testing/fixture.js +1 -1
  117. package/dist/esm/utilities.js +97 -1
  118. package/dist/fast-element.api.json +9804 -5622
  119. package/dist/fast-element.d.ts +813 -2386
  120. package/dist/fast-element.debug.js +2797 -974
  121. package/dist/fast-element.debug.min.js +3 -1
  122. package/dist/fast-element.js +2642 -825
  123. package/dist/fast-element.min.js +3 -1
  124. package/dist/fast-element.untrimmed.d.ts +669 -315
  125. package/docs/{api-report.md → api-report.api.md} +243 -158
  126. package/docs/context/api-report.api.md +69 -0
  127. package/docs/di/api-report.api.md +315 -0
  128. package/karma.conf.cjs +2 -1
  129. package/package.json +59 -47
  130. package/scripts/run-api-extractor.js +51 -0
  131. package/scripts/run-benchmarks.js +46 -0
  132. package/tensile.config.js +12 -0
  133. package/dist/dts/templating/dom.d.ts +0 -41
  134. package/dist/esm/templating/binding.js +0 -282
  135. package/dist/esm/templating/dom.js +0 -49
  136. package/docs/guide/declaring-templates.md +0 -230
  137. package/docs/guide/defining-elements.md +0 -214
  138. package/docs/guide/leveraging-css.md +0 -253
  139. package/docs/guide/next-steps.md +0 -13
  140. package/docs/guide/observables-and-state.md +0 -213
  141. package/docs/guide/using-directives.md +0 -576
  142. package/docs/guide/working-with-shadow-dom.md +0 -296
@@ -1,8 +1,56 @@
1
1
  import { isFunction, isString } from "../interfaces.js";
2
- import { bind, HTMLBindingDirective, oneTime } from "./binding.js";
2
+ import { Binding } from "../binding/binding.js";
3
+ import { FAST, makeSerializationNoop } from "../platform.js";
4
+ import { oneWay } from "../binding/one-way.js";
5
+ import { oneTime } from "../binding/one-time.js";
6
+ import { HTMLBindingDirective } from "./html-binding-directive.js";
3
7
  import { Compiler } from "./compiler.js";
4
- import { Aspect, Binding, HTMLDirective, } from "./html-directive.js";
8
+ import { HTMLDirective, } from "./html-directive.js";
5
9
  import { nextId } from "./markup.js";
10
+ // Much thanks to LitHTML for working this out!
11
+ const lastAttributeNameRegex =
12
+ /* eslint-disable-next-line no-control-regex, max-len */
13
+ /([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
14
+ const noFactories = Object.create(null);
15
+ /**
16
+ * Inlines a template into another template.
17
+ * @public
18
+ */
19
+ export class InlineTemplateDirective {
20
+ /**
21
+ * Creates an instance of InlineTemplateDirective.
22
+ * @param template - The template to inline.
23
+ */
24
+ constructor(html, factories = noFactories) {
25
+ this.html = html;
26
+ this.factories = factories;
27
+ }
28
+ /**
29
+ * Creates HTML to be used within a template.
30
+ * @param add - Can be used to add behavior factories to a template.
31
+ */
32
+ createHTML(add) {
33
+ const factories = this.factories;
34
+ for (const key in factories) {
35
+ add(factories[key]);
36
+ }
37
+ return this.html;
38
+ }
39
+ }
40
+ /**
41
+ * An empty template partial.
42
+ */
43
+ InlineTemplateDirective.empty = new InlineTemplateDirective("");
44
+ HTMLDirective.define(InlineTemplateDirective);
45
+ function createHTML(value, prevString, add, definition = HTMLDirective.getForInstance(value)) {
46
+ if (definition.aspected) {
47
+ const match = lastAttributeNameRegex.exec(prevString);
48
+ if (match !== null) {
49
+ HTMLDirective.assignAspect(value, match[2]);
50
+ }
51
+ }
52
+ return value.createHTML(add);
53
+ }
6
54
  /**
7
55
  * A template capable of creating HTMLView instances or rendering directly to DOM.
8
56
  * @public
@@ -12,21 +60,53 @@ export class ViewTemplate {
12
60
  * Creates an instance of ViewTemplate.
13
61
  * @param html - The html representing what this template will instantiate, including placeholders for directives.
14
62
  * @param factories - The directives that will be connected to placeholders in the html.
63
+ * @param policy - The security policy to use when compiling this template.
15
64
  */
16
- constructor(html, factories) {
65
+ constructor(html, factories = {}, policy) {
66
+ this.policy = policy;
17
67
  this.result = null;
18
68
  this.html = html;
19
69
  this.factories = factories;
20
70
  }
71
+ /**
72
+ * @internal
73
+ */
74
+ compile() {
75
+ if (this.result === null) {
76
+ this.result = Compiler.compile(this.html, this.factories, this.policy);
77
+ }
78
+ return this.result;
79
+ }
21
80
  /**
22
81
  * Creates an HTMLView instance based on this template definition.
23
82
  * @param hostBindingTarget - The element that host behaviors will be bound to.
24
83
  */
25
84
  create(hostBindingTarget) {
26
- if (this.result === null) {
27
- this.result = Compiler.compile(this.html, this.factories);
85
+ return this.compile().createView(hostBindingTarget);
86
+ }
87
+ /**
88
+ * Returns a directive that can inline the template.
89
+ */
90
+ inline() {
91
+ return new InlineTemplateDirective(isString(this.html) ? this.html : this.html.innerHTML, this.factories);
92
+ }
93
+ /**
94
+ * Sets the DOMPolicy for this template.
95
+ * @param policy - The policy to associated with this template.
96
+ * @returns The modified template instance.
97
+ * @remarks
98
+ * The DOMPolicy can only be set once for a template and cannot be
99
+ * set after the template is compiled.
100
+ */
101
+ withPolicy(policy) {
102
+ if (this.result) {
103
+ throw FAST.error(1208 /* Message.cannotSetTemplatePolicyAfterCompilation */);
104
+ }
105
+ if (this.policy) {
106
+ throw FAST.error(1207 /* Message.onlySetTemplatePolicyOnce */);
28
107
  }
29
- return this.result.createView(hostBindingTarget);
108
+ this.policy = policy;
109
+ return this;
30
110
  }
31
111
  /**
32
112
  * Creates an HTMLView from this template, binds it to the source, and then appends it to the host.
@@ -41,18 +121,49 @@ export class ViewTemplate {
41
121
  view.appendTo(host);
42
122
  return view;
43
123
  }
44
- }
45
- // Much thanks to LitHTML for working this out!
46
- const lastAttributeNameRegex =
47
- /* eslint-disable-next-line no-control-regex */
48
- /([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
49
- function createAspectedHTML(value, prevString, add) {
50
- const match = lastAttributeNameRegex.exec(prevString);
51
- if (match !== null) {
52
- Aspect.assign(value, match[2]);
124
+ /**
125
+ * Creates a template based on a set of static strings and dynamic values.
126
+ * @param strings - The static strings to create the template with.
127
+ * @param values - The dynamic values to create the template with.
128
+ * @param policy - The DOMPolicy to associated with the template.
129
+ * @returns A ViewTemplate.
130
+ * @remarks
131
+ * This API should not be used directly under normal circumstances because constructing
132
+ * a template in this way, if not done properly, can open up the application to XSS
133
+ * attacks. When using this API, provide a strong DOMPolicy that can properly sanitize
134
+ * and also be sure to manually sanitize all static strings particularly if they can
135
+ * come from user input.
136
+ */
137
+ static create(strings, values, policy) {
138
+ let html = "";
139
+ const factories = Object.create(null);
140
+ const add = (factory) => {
141
+ var _a;
142
+ const id = (_a = factory.id) !== null && _a !== void 0 ? _a : (factory.id = nextId());
143
+ factories[id] = factory;
144
+ return id;
145
+ };
146
+ for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
147
+ const currentString = strings[i];
148
+ let currentValue = values[i];
149
+ let definition;
150
+ html += currentString;
151
+ if (isFunction(currentValue)) {
152
+ currentValue = new HTMLBindingDirective(oneWay(currentValue));
153
+ }
154
+ else if (currentValue instanceof Binding) {
155
+ currentValue = new HTMLBindingDirective(currentValue);
156
+ }
157
+ else if (!(definition = HTMLDirective.getForInstance(currentValue))) {
158
+ const staticValue = currentValue;
159
+ currentValue = new HTMLBindingDirective(oneTime(() => staticValue));
160
+ }
161
+ html += createHTML(currentValue, currentString, add, definition);
162
+ }
163
+ return new ViewTemplate(html + strings[strings.length - 1], factories, policy);
53
164
  }
54
- return value.createHTML(add);
55
165
  }
166
+ makeSerializationNoop(ViewTemplate);
56
167
  /**
57
168
  * Transforms a template literal string into a ViewTemplate.
58
169
  * @param strings - The string fragments that are interpolated with the values.
@@ -62,48 +173,12 @@ function createAspectedHTML(value, prevString, add) {
62
173
  * other template instances, and Directive instances.
63
174
  * @public
64
175
  */
65
- export function html(strings, ...values) {
66
- let html = "";
67
- const factories = Object.create(null);
68
- const add = (factory) => {
69
- var _a;
70
- const id = (_a = factory.id) !== null && _a !== void 0 ? _a : (factory.id = nextId());
71
- factories[id] = factory;
72
- return id;
73
- };
74
- for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
75
- const currentString = strings[i];
76
- const currentValue = values[i];
77
- let definition;
78
- html += currentString;
79
- if (isFunction(currentValue)) {
80
- html += createAspectedHTML(new HTMLBindingDirective(bind(currentValue)), currentString, add);
81
- }
82
- else if (isString(currentValue)) {
83
- const match = lastAttributeNameRegex.exec(currentString);
84
- if (match !== null) {
85
- const directive = new HTMLBindingDirective(oneTime(() => currentValue));
86
- Aspect.assign(directive, match[2]);
87
- html += directive.createHTML(add);
88
- }
89
- else {
90
- html += currentValue;
91
- }
92
- }
93
- else if (currentValue instanceof Binding) {
94
- html += createAspectedHTML(new HTMLBindingDirective(currentValue), currentString, add);
95
- }
96
- else if ((definition = HTMLDirective.getForInstance(currentValue)) === void 0) {
97
- html += createAspectedHTML(new HTMLBindingDirective(oneTime(() => currentValue)), currentString, add);
98
- }
99
- else {
100
- if (definition.aspected) {
101
- html += createAspectedHTML(currentValue, currentString, add);
102
- }
103
- else {
104
- html += currentValue.createHTML(add);
105
- }
106
- }
176
+ export const html = ((strings, ...values) => {
177
+ if (Array.isArray(strings) && Array.isArray(strings.raw)) {
178
+ return ViewTemplate.create(strings, values);
107
179
  }
108
- return new ViewTemplate(html + strings[strings.length - 1], factories);
109
- }
180
+ throw FAST.error(1206 /* Message.directCallToHTMLTagNotAllowed */);
181
+ });
182
+ html.partial = (html) => {
183
+ return new InlineTemplateDirective(html);
184
+ };
@@ -1,37 +1,24 @@
1
- import { ExecutionContext, Observable } from "../observation/observable.js";
1
+ var _a;
2
+ import { Hydratable } from "../components/hydration.js";
3
+ import { buildViewBindingTargets, createRangeForNodes, HydrationTargetElementError, targetFactory, } from "../hydration/target-builder.js";
4
+ import { ExecutionContext, Observable, SourceLifetime, } from "../observation/observable.js";
5
+ import { makeSerializationNoop } from "../platform.js";
2
6
  function removeNodeSequence(firstNode, lastNode) {
3
7
  const parent = firstNode.parentNode;
4
8
  let current = firstNode;
5
9
  let next;
6
10
  while (current !== lastNode) {
7
11
  next = current.nextSibling;
12
+ if (!next) {
13
+ throw new Error(`Unmatched first/last child inside "${lastNode.getRootNode().host.nodeName}".`);
14
+ }
8
15
  parent.removeChild(current);
9
16
  current = next;
10
17
  }
11
18
  parent.removeChild(lastNode);
12
19
  }
13
- /**
14
- * The standard View implementation, which also implements ElementView and SyntheticView.
15
- * @public
16
- */
17
- export class HTMLView {
18
- /**
19
- * Constructs an instance of HTMLView.
20
- * @param fragment - The html fragment that contains the nodes for this view.
21
- * @param behaviors - The behaviors to be applied to this view.
22
- */
23
- constructor(fragment, factories, targets) {
24
- this.fragment = fragment;
25
- this.factories = factories;
26
- this.targets = targets;
27
- this.behaviors = null;
28
- this.unbindables = [];
29
- /**
30
- * The data that the view is bound to.
31
- */
32
- this.source = null;
33
- this.isBound = false;
34
- this.selfContained = false;
20
+ class DefaultExecutionContext {
21
+ constructor() {
35
22
  /**
36
23
  * The index of the current item within a repeat context.
37
24
  */
@@ -40,14 +27,6 @@ export class HTMLView {
40
27
  * The length of the current collection within a repeat context.
41
28
  */
42
29
  this.length = 0;
43
- this.firstChild = fragment.firstChild;
44
- this.lastChild = fragment.lastChild;
45
- }
46
- /**
47
- * The execution context the view is running within.
48
- */
49
- get context() {
50
- return this;
51
30
  }
52
31
  /**
53
32
  * The current event within an event handler.
@@ -102,6 +81,43 @@ export class HTMLView {
102
81
  eventTarget() {
103
82
  return this.event.target;
104
83
  }
84
+ }
85
+ /**
86
+ * The standard View implementation, which also implements ElementView and SyntheticView.
87
+ * @public
88
+ */
89
+ export class HTMLView extends DefaultExecutionContext {
90
+ /**
91
+ * Constructs an instance of HTMLView.
92
+ * @param fragment - The html fragment that contains the nodes for this view.
93
+ * @param behaviors - The behaviors to be applied to this view.
94
+ */
95
+ constructor(fragment, factories, targets) {
96
+ super();
97
+ this.fragment = fragment;
98
+ this.factories = factories;
99
+ this.targets = targets;
100
+ this.behaviors = null;
101
+ this.unbindables = [];
102
+ /**
103
+ * The data that the view is bound to.
104
+ */
105
+ this.source = null;
106
+ /**
107
+ * Indicates whether the controller is bound.
108
+ */
109
+ this.isBound = false;
110
+ /**
111
+ * Indicates how the source's lifetime relates to the controller's lifetime.
112
+ */
113
+ this.sourceLifetime = SourceLifetime.unknown;
114
+ /**
115
+ * The execution context the view is running within.
116
+ */
117
+ this.context = this;
118
+ this.firstChild = fragment.firstChild;
119
+ this.lastChild = fragment.lastChild;
120
+ }
105
121
  /**
106
122
  * Appends the view's DOM nodes to the referenced node.
107
123
  * @param node - The parent node to append the view's DOM nodes to.
@@ -164,13 +180,14 @@ export class HTMLView {
164
180
  * @param source - The binding source for the view's binding behaviors.
165
181
  * @param context - The execution context to run the behaviors within.
166
182
  */
167
- bind(source) {
183
+ bind(source, context = this) {
168
184
  if (this.source === source) {
169
185
  return;
170
186
  }
171
187
  let behaviors = this.behaviors;
172
188
  if (behaviors === null) {
173
189
  this.source = source;
190
+ this.context = context;
174
191
  this.behaviors = behaviors = new Array(this.factories.length);
175
192
  const factories = this.factories;
176
193
  for (let i = 0, ii = factories.length; i < ii; ++i) {
@@ -185,6 +202,7 @@ export class HTMLView {
185
202
  }
186
203
  this.isBound = false;
187
204
  this.source = source;
205
+ this.context = context;
188
206
  for (let i = 0, ii = behaviors.length; i < ii; ++i) {
189
207
  behaviors[i].bind(this);
190
208
  }
@@ -200,6 +218,7 @@ export class HTMLView {
200
218
  }
201
219
  this.evaluateUnbindables();
202
220
  this.source = null;
221
+ this.context = this;
203
222
  this.isBound = false;
204
223
  }
205
224
  evaluateUnbindables() {
@@ -223,5 +242,213 @@ export class HTMLView {
223
242
  }
224
243
  }
225
244
  }
245
+ makeSerializationNoop(HTMLView);
226
246
  Observable.defineProperty(HTMLView.prototype, "index");
227
247
  Observable.defineProperty(HTMLView.prototype, "length");
248
+ export const HydrationStage = {
249
+ unhydrated: "unhydrated",
250
+ hydrating: "hydrating",
251
+ hydrated: "hydrated",
252
+ };
253
+ /** @public */
254
+ export class HydrationBindingError extends Error {
255
+ constructor(
256
+ /**
257
+ * The error message
258
+ */
259
+ message,
260
+ /**
261
+ * The factory that was unable to be bound
262
+ */
263
+ factory,
264
+ /**
265
+ * A DocumentFragment containing a clone of the
266
+ * view's Nodes.
267
+ */
268
+ fragment,
269
+ /**
270
+ * String representation of the HTML in the template that
271
+ * threw the binding error.
272
+ */
273
+ templateString) {
274
+ super(message);
275
+ this.factory = factory;
276
+ this.fragment = fragment;
277
+ this.templateString = templateString;
278
+ }
279
+ }
280
+ export class HydrationView extends DefaultExecutionContext {
281
+ constructor(firstChild, lastChild, sourceTemplate, hostBindingTarget) {
282
+ super();
283
+ this.firstChild = firstChild;
284
+ this.lastChild = lastChild;
285
+ this.sourceTemplate = sourceTemplate;
286
+ this.hostBindingTarget = hostBindingTarget;
287
+ this[_a] = Hydratable;
288
+ this.context = this;
289
+ this.source = null;
290
+ this.isBound = false;
291
+ this.sourceLifetime = SourceLifetime.unknown;
292
+ this.unbindables = [];
293
+ this.fragment = null;
294
+ this.behaviors = null;
295
+ this._hydrationStage = HydrationStage.unhydrated;
296
+ this._bindingViewBoundaries = {};
297
+ this._targets = {};
298
+ this.factories = sourceTemplate.compile().factories;
299
+ }
300
+ get hydrationStage() {
301
+ return this._hydrationStage;
302
+ }
303
+ get targets() {
304
+ return this._targets;
305
+ }
306
+ get bindingViewBoundaries() {
307
+ return this._bindingViewBoundaries;
308
+ }
309
+ /**
310
+ * no-op. Hydrated views are don't need to be moved from a documentFragment
311
+ * to the target node.
312
+ */
313
+ insertBefore(node) {
314
+ // No-op in cases where this is called before the view is removed,
315
+ // because the nodes will already be in the document and just need hydrating.
316
+ if (this.fragment === null) {
317
+ return;
318
+ }
319
+ if (this.fragment.hasChildNodes()) {
320
+ node.parentNode.insertBefore(this.fragment, node);
321
+ }
322
+ else {
323
+ const end = this.lastChild;
324
+ if (node.previousSibling === end)
325
+ return;
326
+ const parentNode = node.parentNode;
327
+ let current = this.firstChild;
328
+ let next;
329
+ while (current !== end) {
330
+ next = current.nextSibling;
331
+ parentNode.insertBefore(current, node);
332
+ current = next;
333
+ }
334
+ parentNode.insertBefore(end, node);
335
+ }
336
+ }
337
+ /**
338
+ * Appends the view to a node. In cases where this is called before the
339
+ * view has been removed, the method will no-op.
340
+ * @param node - the node to append the view to.
341
+ */
342
+ appendTo(node) {
343
+ if (this.fragment !== null) {
344
+ node.appendChild(this.fragment);
345
+ }
346
+ }
347
+ remove() {
348
+ const fragment = this.fragment || (this.fragment = document.createDocumentFragment());
349
+ const end = this.lastChild;
350
+ let current = this.firstChild;
351
+ let next;
352
+ while (current !== end) {
353
+ next = current.nextSibling;
354
+ if (!next) {
355
+ throw new Error(`Unmatched first/last child inside "${end.getRootNode().host.nodeName}".`);
356
+ }
357
+ fragment.appendChild(current);
358
+ current = next;
359
+ }
360
+ fragment.appendChild(end);
361
+ }
362
+ bind(source, context = this) {
363
+ var _b, _c;
364
+ if (this.hydrationStage !== HydrationStage.hydrated) {
365
+ this._hydrationStage = HydrationStage.hydrating;
366
+ }
367
+ if (this.source === source) {
368
+ return;
369
+ }
370
+ let behaviors = this.behaviors;
371
+ if (behaviors === null) {
372
+ this.source = source;
373
+ this.context = context;
374
+ try {
375
+ const { targets, boundaries } = buildViewBindingTargets(this.firstChild, this.lastChild, this.factories);
376
+ this._targets = targets;
377
+ this._bindingViewBoundaries = boundaries;
378
+ }
379
+ catch (error) {
380
+ if (error instanceof HydrationTargetElementError) {
381
+ let templateString = this.sourceTemplate.html;
382
+ if (typeof templateString !== "string") {
383
+ templateString = templateString.innerHTML;
384
+ }
385
+ error.templateString = templateString;
386
+ }
387
+ throw error;
388
+ }
389
+ this.behaviors = behaviors = new Array(this.factories.length);
390
+ const factories = this.factories;
391
+ for (let i = 0, ii = factories.length; i < ii; ++i) {
392
+ const factory = factories[i];
393
+ if (factory.targetNodeId === "h" && this.hostBindingTarget) {
394
+ targetFactory(factory, this.hostBindingTarget, this._targets);
395
+ }
396
+ // If the binding has been targeted or it is a host binding and the view has a hostBindingTarget
397
+ if (factory.targetNodeId in this.targets) {
398
+ const behavior = factory.createBehavior();
399
+ behavior.bind(this);
400
+ behaviors[i] = behavior;
401
+ }
402
+ else {
403
+ let templateString = this.sourceTemplate.html;
404
+ if (typeof templateString !== "string") {
405
+ templateString = templateString.innerHTML;
406
+ }
407
+ throw new HydrationBindingError(`HydrationView was unable to successfully target bindings inside "${(_c = ((_b = this.firstChild) === null || _b === void 0 ? void 0 : _b.getRootNode()).host) === null || _c === void 0 ? void 0 : _c.nodeName}".`, factory, createRangeForNodes(this.firstChild, this.lastChild).cloneContents(), templateString);
408
+ }
409
+ }
410
+ }
411
+ else {
412
+ if (this.source !== null) {
413
+ this.evaluateUnbindables();
414
+ }
415
+ this.isBound = false;
416
+ this.source = source;
417
+ this.context = context;
418
+ for (let i = 0, ii = behaviors.length; i < ii; ++i) {
419
+ behaviors[i].bind(this);
420
+ }
421
+ }
422
+ this.isBound = true;
423
+ this._hydrationStage = HydrationStage.hydrated;
424
+ }
425
+ unbind() {
426
+ if (!this.isBound || this.source === null) {
427
+ return;
428
+ }
429
+ this.evaluateUnbindables();
430
+ this.source = null;
431
+ this.context = this;
432
+ this.isBound = false;
433
+ }
434
+ /**
435
+ * Removes the view and unbinds its behaviors, disposing of DOM nodes afterward.
436
+ * Once a view has been disposed, it cannot be inserted or bound again.
437
+ */
438
+ dispose() {
439
+ removeNodeSequence(this.firstChild, this.lastChild);
440
+ this.unbind();
441
+ }
442
+ onUnbind(behavior) {
443
+ this.unbindables.push(behavior);
444
+ }
445
+ evaluateUnbindables() {
446
+ const unbindables = this.unbindables;
447
+ for (let i = 0, ii = unbindables.length; i < ii; ++i) {
448
+ unbindables[i].unbind(this);
449
+ }
450
+ unbindables.length = 0;
451
+ }
452
+ }
453
+ _a = Hydratable;
454
+ makeSerializationNoop(HydrationView);
@@ -0,0 +1,36 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { attr, FASTElement, html, when } from "../index.js";
11
+ class TestWhen extends FASTElement {
12
+ constructor() {
13
+ super(...arguments);
14
+ this.try = false;
15
+ }
16
+ }
17
+ __decorate([
18
+ attr({
19
+ mode: "boolean",
20
+ }),
21
+ __metadata("design:type", Boolean)
22
+ ], TestWhen.prototype, "try", void 0);
23
+ TestWhen.define({
24
+ name: "test-when",
25
+ template: html `
26
+ ${when(x => x.try, html `
27
+ <span>Yes</span>
28
+ `)}
29
+ `,
30
+ });
31
+ const itemRenderer = () => {
32
+ const testWhen = document.createElement("test-when");
33
+ return testWhen;
34
+ };
35
+ export default itemRenderer;
36
+ export { tests } from "@tensile-perf/web-components";
@@ -0,0 +1,39 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { attr, FASTElement, html, when } from "../index.js";
11
+ class TestWhen extends FASTElement {
12
+ constructor() {
13
+ super(...arguments);
14
+ this.try = false;
15
+ }
16
+ }
17
+ __decorate([
18
+ attr({
19
+ mode: "boolean",
20
+ }),
21
+ __metadata("design:type", Boolean)
22
+ ], TestWhen.prototype, "try", void 0);
23
+ TestWhen.define({
24
+ name: "test-when",
25
+ template: html `
26
+ ${when(x => x.try, html `
27
+ <span>Yes</span>
28
+ `)}
29
+ ${when(x => !x.try, html `
30
+ <span>No</span>
31
+ `)}
32
+ `,
33
+ });
34
+ const itemRenderer = () => {
35
+ const testWhen = document.createElement("test-when");
36
+ return testWhen;
37
+ };
38
+ export default itemRenderer;
39
+ export { tests } from "@tensile-perf/web-components";