@microsoft/fast-element 2.10.4 → 3.0.0-rc.2

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 (211) hide show
  1. package/CHANGELOG.md +52 -2
  2. package/README.md +244 -1
  3. package/dist/arrays/arrays.api.json +2621 -0
  4. package/dist/context/context.api.json +13 -13
  5. package/dist/declarative/declarative.api.json +8483 -0
  6. package/dist/di/di.api.json +16 -16
  7. package/dist/dts/__test__/helpers.d.ts +6 -0
  8. package/dist/dts/array-observer.d.ts +2 -0
  9. package/dist/dts/arrays.d.ts +2 -0
  10. package/dist/dts/attr.d.ts +1 -0
  11. package/dist/dts/binding/binding.d.ts +15 -5
  12. package/dist/dts/binding/one-time.d.ts +1 -1
  13. package/dist/dts/binding/one-way.d.ts +1 -1
  14. package/dist/dts/binding/signal.d.ts +6 -6
  15. package/dist/dts/binding/two-way.d.ts +2 -1
  16. package/dist/dts/binding.d.ts +7 -0
  17. package/dist/dts/components/attributes.d.ts +1 -4
  18. package/dist/dts/components/definition-schema-transforms.d.ts +9 -0
  19. package/dist/dts/components/element-controller.d.ts +80 -114
  20. package/dist/dts/components/element-hydration.d.ts +1 -1
  21. package/dist/dts/components/enable-hydration.d.ts +54 -0
  22. package/dist/dts/components/fast-definitions.d.ts +98 -46
  23. package/dist/dts/components/fast-element.d.ts +43 -16
  24. package/dist/dts/components/hydration-tracker.d.ts +83 -0
  25. package/dist/dts/components/hydration.d.ts +23 -53
  26. package/dist/dts/components/schema.d.ts +205 -0
  27. package/dist/dts/context.d.ts +13 -13
  28. package/dist/dts/css.d.ts +3 -0
  29. package/dist/dts/debug.d.ts +5 -1
  30. package/dist/dts/declarative/attribute-map.d.ts +58 -0
  31. package/dist/dts/declarative/debug.d.ts +4 -0
  32. package/dist/dts/declarative/index.d.ts +14 -0
  33. package/dist/dts/declarative/interfaces.d.ts +8 -0
  34. package/dist/dts/declarative/observer-map-utilities.d.ts +58 -0
  35. package/dist/dts/declarative/observer-map.d.ts +89 -0
  36. package/dist/dts/declarative/runtime.d.ts +5 -0
  37. package/dist/dts/declarative/syntax.d.ts +21 -0
  38. package/dist/dts/declarative/template-bridge.d.ts +33 -0
  39. package/dist/dts/declarative/template-parser.d.ts +98 -0
  40. package/dist/dts/declarative/template.d.ts +10 -0
  41. package/dist/dts/declarative/utilities.d.ts +358 -0
  42. package/dist/dts/di/di.d.ts +7 -7
  43. package/dist/dts/directives/children.d.ts +2 -0
  44. package/dist/dts/directives/node-observation.d.ts +2 -0
  45. package/dist/dts/directives/ref.d.ts +2 -0
  46. package/dist/dts/directives/repeat.d.ts +4 -0
  47. package/dist/dts/directives/slotted.d.ts +2 -0
  48. package/dist/dts/directives/when.d.ts +3 -0
  49. package/dist/dts/dom-policy.d.ts +23 -5
  50. package/dist/dts/dom.d.ts +4 -16
  51. package/dist/dts/html.d.ts +5 -0
  52. package/dist/dts/hydration/diagnostics.d.ts +93 -0
  53. package/dist/dts/hydration/hydration-debugger.d.ts +35 -0
  54. package/dist/dts/hydration/messages.d.ts +62 -0
  55. package/dist/dts/hydration/runtime.d.ts +7 -0
  56. package/dist/dts/hydration/target-builder.d.ts +40 -12
  57. package/dist/dts/hydration.d.ts +18 -0
  58. package/dist/dts/index.d.ts +42 -42
  59. package/dist/dts/index.debug.d.ts +0 -1
  60. package/dist/dts/index.rollup.debug.d.ts +0 -1
  61. package/dist/dts/interfaces.d.ts +2 -49
  62. package/dist/dts/observable.d.ts +3 -6
  63. package/dist/dts/observation/arrays.d.ts +1 -1
  64. package/dist/dts/observation/observable.d.ts +3 -3
  65. package/dist/dts/observation/update-queue.d.ts +1 -1
  66. package/dist/dts/platform.d.ts +45 -8
  67. package/dist/dts/registry.d.ts +1 -0
  68. package/dist/dts/render.d.ts +7 -0
  69. package/dist/dts/schema.d.ts +1 -0
  70. package/dist/dts/state/exports.d.ts +1 -1
  71. package/dist/dts/state/state.d.ts +2 -2
  72. package/dist/dts/styles/css-directive.d.ts +5 -12
  73. package/dist/dts/styles/css.d.ts +5 -7
  74. package/dist/dts/styles/element-styles.d.ts +0 -10
  75. package/dist/dts/styles.d.ts +6 -0
  76. package/dist/dts/templating/compiler.d.ts +1 -1
  77. package/dist/dts/templating/html-binding-directive.d.ts +10 -2
  78. package/dist/dts/templating/html-directive.d.ts +19 -1
  79. package/dist/dts/templating/hydration-view.d.ts +130 -0
  80. package/dist/dts/templating/render.d.ts +1 -1
  81. package/dist/dts/templating/repeat.d.ts +1 -1
  82. package/dist/dts/templating/template.d.ts +15 -7
  83. package/dist/dts/templating/view.d.ts +25 -102
  84. package/dist/dts/templating.d.ts +10 -0
  85. package/dist/dts/testing/exports.d.ts +2 -2
  86. package/dist/dts/testing/fakes.d.ts +4 -4
  87. package/dist/dts/updates.d.ts +1 -0
  88. package/dist/dts/volatile.d.ts +2 -0
  89. package/dist/esm/__test__/helpers.js +22 -0
  90. package/dist/esm/__test__/setup-node.js +18 -0
  91. package/dist/esm/array-observer.js +1 -0
  92. package/dist/esm/arrays.js +1 -0
  93. package/dist/esm/attr.js +1 -0
  94. package/dist/esm/binding/normalize.js +1 -1
  95. package/dist/esm/binding/signal.js +4 -4
  96. package/dist/esm/binding/two-way.js +3 -3
  97. package/dist/esm/binding.js +4 -0
  98. package/dist/esm/components/attributes.js +18 -11
  99. package/dist/esm/components/definition-schema-transforms.js +23 -0
  100. package/dist/esm/components/element-controller.js +206 -270
  101. package/dist/esm/components/element-hydration.js +1 -1
  102. package/dist/esm/components/enable-hydration.js +124 -0
  103. package/dist/esm/components/fast-definitions.js +219 -56
  104. package/dist/esm/components/fast-element.js +18 -27
  105. package/dist/esm/components/hydration-tracker.js +122 -0
  106. package/dist/esm/components/hydration.js +137 -140
  107. package/dist/esm/components/schema.js +253 -0
  108. package/dist/esm/context.js +6 -6
  109. package/dist/esm/css.js +3 -0
  110. package/dist/esm/debug.js +27 -26
  111. package/dist/esm/declarative/attribute-map.js +122 -0
  112. package/dist/esm/declarative/debug.js +4 -0
  113. package/dist/esm/declarative/index.js +4 -0
  114. package/dist/esm/declarative/interfaces.js +9 -0
  115. package/dist/esm/declarative/observer-map-utilities.js +565 -0
  116. package/dist/esm/declarative/observer-map.js +216 -0
  117. package/dist/esm/declarative/runtime.js +14 -0
  118. package/dist/esm/declarative/syntax.js +36 -0
  119. package/dist/esm/declarative/template-bridge.js +160 -0
  120. package/dist/esm/declarative/template-parser.js +306 -0
  121. package/dist/esm/declarative/template.js +143 -0
  122. package/dist/esm/declarative/utilities.js +1069 -0
  123. package/dist/esm/di/di.js +8 -9
  124. package/dist/esm/directives/children.js +1 -0
  125. package/dist/esm/directives/node-observation.js +1 -0
  126. package/dist/esm/directives/ref.js +1 -0
  127. package/dist/esm/directives/repeat.js +1 -0
  128. package/dist/esm/directives/slotted.js +1 -0
  129. package/dist/esm/directives/when.js +1 -0
  130. package/dist/esm/dom-policy.js +35 -6
  131. package/dist/esm/dom.js +1 -1
  132. package/dist/esm/html.js +2 -0
  133. package/dist/esm/hydration/diagnostics.js +50 -0
  134. package/dist/esm/hydration/hydration-debugger.js +112 -0
  135. package/dist/esm/hydration/messages.js +84 -0
  136. package/dist/esm/hydration/runtime.js +33 -0
  137. package/dist/esm/hydration/target-builder.js +144 -91
  138. package/dist/esm/hydration.js +6 -0
  139. package/dist/esm/index.debug.js +2 -1
  140. package/dist/esm/index.js +38 -29
  141. package/dist/esm/index.rollup.debug.js +3 -2
  142. package/dist/esm/index.rollup.js +1 -1
  143. package/dist/esm/interfaces.js +2 -45
  144. package/dist/esm/metadata.js +2 -8
  145. package/dist/esm/observable.js +1 -4
  146. package/dist/esm/observation/arrays.js +1 -1
  147. package/dist/esm/observation/notifier.js +2 -4
  148. package/dist/esm/observation/observable.js +5 -5
  149. package/dist/esm/observation/update-queue.js +47 -58
  150. package/dist/esm/platform.js +31 -30
  151. package/dist/esm/registry.js +1 -0
  152. package/dist/esm/render.js +1 -0
  153. package/dist/esm/schema.js +1 -0
  154. package/dist/esm/state/exports.js +1 -1
  155. package/dist/esm/styles/css-directive.js +1 -2
  156. package/dist/esm/styles/css.js +15 -56
  157. package/dist/esm/styles/element-styles.js +69 -15
  158. package/dist/esm/styles.js +2 -0
  159. package/dist/esm/templating/html-binding-directive.js +11 -9
  160. package/dist/esm/templating/hydration-view.js +228 -0
  161. package/dist/esm/templating/render.js +39 -18
  162. package/dist/esm/templating/repeat.js +69 -33
  163. package/dist/esm/templating/template.js +7 -7
  164. package/dist/esm/templating/view.js +25 -234
  165. package/dist/esm/templating.js +7 -0
  166. package/dist/esm/testing/exports.js +2 -2
  167. package/dist/esm/testing/fixture.js +2 -2
  168. package/dist/esm/testing/timeout.js +2 -2
  169. package/dist/esm/updates.js +1 -0
  170. package/dist/esm/volatile.js +1 -0
  171. package/dist/fast-element.api.json +14389 -11138
  172. package/dist/fast-element.d.ts +3651 -809
  173. package/dist/fast-element.debug.js +5666 -4722
  174. package/dist/fast-element.debug.min.js +2 -2
  175. package/dist/fast-element.js +5394 -4381
  176. package/dist/fast-element.min.js +2 -2
  177. package/dist/fast-element.untrimmed.d.ts +923 -472
  178. package/dist/hydration/hydration.api.json +6460 -0
  179. package/dist/styles/styles.api.json +2672 -0
  180. package/package.json +165 -45
  181. package/ARCHITECTURE_FASTELEMENT.md +0 -63
  182. package/ARCHITECTURE_HTML_TAGGED_TEMPLATE_LITERAL.md +0 -36
  183. package/ARCHITECTURE_INTRO.md +0 -10
  184. package/ARCHITECTURE_OVERVIEW.md +0 -52
  185. package/ARCHITECTURE_UPDATES.md +0 -11
  186. package/CHANGELOG.json +0 -2275
  187. package/DESIGN.md +0 -510
  188. package/api-extractor.context.json +0 -14
  189. package/api-extractor.di.json +0 -14
  190. package/biome.json +0 -4
  191. package/dist/dts/components/install-hydration.d.ts +0 -1
  192. package/dist/dts/pending-task.d.ts +0 -32
  193. package/dist/dts/styles/css-binding-directive.d.ts +0 -60
  194. package/dist/dts/templating/install-hydratable-view-templates.d.ts +0 -1
  195. package/dist/esm/components/install-hydration.js +0 -3
  196. package/dist/esm/pending-task.js +0 -28
  197. package/dist/esm/polyfills.js +0 -60
  198. package/dist/esm/styles/css-binding-directive.js +0 -76
  199. package/dist/esm/templating/install-hydratable-view-templates.js +0 -23
  200. package/docs/ACKNOWLEDGEMENTS.md +0 -12
  201. package/docs/api-report.api.md +0 -1122
  202. package/docs/context/api-report.api.md +0 -69
  203. package/docs/di/api-report.api.md +0 -315
  204. package/docs/fast-element-2-changes.md +0 -15
  205. package/playwright.config.ts +0 -26
  206. package/scripts/run-api-extractor.js +0 -51
  207. package/test/index.html +0 -11
  208. package/test/main.ts +0 -104
  209. package/test/vite.config.ts +0 -19
  210. package/tsconfig.api-extractor.json +0 -6
  211. /package/dist/dts/{polyfills.d.ts → __test__/setup-node.d.ts} +0 -0
@@ -0,0 +1,228 @@
1
+ var _a;
2
+ import { Hydratable } from "../components/hydration.js";
3
+ import { getHostName, getHydrationDiagnostic, } from "../hydration/diagnostics.js";
4
+ import { buildViewBindingTargets, createRangeForNodes, HydrationTargetElementError, targetFactory, } from "../hydration/target-builder.js";
5
+ import { SourceLifetime } from "../observation/observable.js";
6
+ import { makeSerializationNoop } from "../platform.js";
7
+ import { DefaultExecutionContext, removeNodeSequence, } from "./view.js";
8
+ /** @public */
9
+ export const HydrationStage = {
10
+ unhydrated: "unhydrated",
11
+ hydrating: "hydrating",
12
+ hydrated: "hydrated",
13
+ };
14
+ /** @public */
15
+ export class HydrationBindingError extends Error {
16
+ constructor(
17
+ /**
18
+ * The error message
19
+ */
20
+ message,
21
+ /**
22
+ * The factory that was unable to be bound
23
+ */
24
+ factory,
25
+ /**
26
+ * A DocumentFragment containing a clone of the
27
+ * view's Nodes.
28
+ */
29
+ fragment,
30
+ /**
31
+ * String representation of the HTML in the template that
32
+ * threw the binding error.
33
+ */
34
+ templateString,
35
+ /**
36
+ * Structured description of the binding the hydration walk was
37
+ * attempting to apply when the mismatch was detected.
38
+ */
39
+ expected,
40
+ /**
41
+ * Structured description of the server-rendered DOM that was
42
+ * encountered at the mismatch point.
43
+ */
44
+ received) {
45
+ super(message);
46
+ this.factory = factory;
47
+ this.fragment = fragment;
48
+ this.templateString = templateString;
49
+ this.expected = expected;
50
+ this.received = received;
51
+ }
52
+ }
53
+ export class HydrationView extends DefaultExecutionContext {
54
+ get hydrationStage() {
55
+ return this._hydrationStage;
56
+ }
57
+ get targets() {
58
+ return this._targets;
59
+ }
60
+ get bindingViewBoundaries() {
61
+ return this._bindingViewBoundaries;
62
+ }
63
+ constructor(firstChild, lastChild, sourceTemplate, hostBindingTarget) {
64
+ super();
65
+ this.firstChild = firstChild;
66
+ this.lastChild = lastChild;
67
+ this.sourceTemplate = sourceTemplate;
68
+ this.hostBindingTarget = hostBindingTarget;
69
+ this[_a] = Hydratable;
70
+ this.context = this;
71
+ this.source = null;
72
+ this.isBound = false;
73
+ this.sourceLifetime = SourceLifetime.unknown;
74
+ this.unbindables = [];
75
+ this.fragment = null;
76
+ this.behaviors = null;
77
+ this._hydrationStage = HydrationStage.unhydrated;
78
+ this._bindingViewBoundaries = {};
79
+ this._targets = {};
80
+ this.factories = sourceTemplate.compile().factories;
81
+ }
82
+ /**
83
+ * no-op. Hydrated views are don't need to be moved from a documentFragment
84
+ * to the target node.
85
+ */
86
+ insertBefore(node) {
87
+ // No-op in cases where this is called before the view is removed,
88
+ // because the nodes will already be in the document and just need hydrating.
89
+ if (this.fragment === null) {
90
+ return;
91
+ }
92
+ if (this.fragment.hasChildNodes()) {
93
+ node.parentNode.insertBefore(this.fragment, node);
94
+ }
95
+ else {
96
+ const end = this.lastChild;
97
+ if (node.previousSibling === end)
98
+ return;
99
+ const parentNode = node.parentNode;
100
+ let current = this.firstChild;
101
+ let next;
102
+ while (current !== end) {
103
+ next = current.nextSibling;
104
+ parentNode.insertBefore(current, node);
105
+ current = next;
106
+ }
107
+ parentNode.insertBefore(end, node);
108
+ }
109
+ }
110
+ /**
111
+ * Appends the view to a node. In cases where this is called before the
112
+ * view has been removed, the method will no-op.
113
+ * @param node - the node to append the view to.
114
+ */
115
+ appendTo(node) {
116
+ if (this.fragment !== null) {
117
+ node.appendChild(this.fragment);
118
+ }
119
+ }
120
+ remove() {
121
+ const fragment = this.fragment || (this.fragment = document.createDocumentFragment());
122
+ const end = this.lastChild;
123
+ let current = this.firstChild;
124
+ let next;
125
+ while (current !== end) {
126
+ next = current.nextSibling;
127
+ if (!next) {
128
+ throw new Error(`Unmatched first/last child inside "${end.getRootNode().host.nodeName}".`);
129
+ }
130
+ fragment.appendChild(current);
131
+ current = next;
132
+ }
133
+ fragment.appendChild(end);
134
+ }
135
+ bind(source, context = this) {
136
+ if (this.source === source && this.context === context) {
137
+ return;
138
+ }
139
+ if (this.hydrationStage !== HydrationStage.hydrated) {
140
+ this._hydrationStage = HydrationStage.hydrating;
141
+ }
142
+ let behaviors = this.behaviors;
143
+ if (behaviors === null) {
144
+ this.source = source;
145
+ this.context = context;
146
+ try {
147
+ const { targets, boundaries } = buildViewBindingTargets(this.firstChild, this.lastChild, this.factories);
148
+ this._targets = targets;
149
+ this._bindingViewBoundaries = boundaries;
150
+ }
151
+ catch (error) {
152
+ if (error instanceof HydrationTargetElementError) {
153
+ let templateString = this.sourceTemplate.html;
154
+ if (typeof templateString !== "string") {
155
+ templateString = templateString.innerHTML;
156
+ }
157
+ error.templateString = templateString;
158
+ }
159
+ throw error;
160
+ }
161
+ this.behaviors = behaviors = new Array(this.factories.length);
162
+ const factories = this.factories;
163
+ for (let i = 0, ii = factories.length; i < ii; ++i) {
164
+ const factory = factories[i];
165
+ if (factory.targetNodeId === "h" && this.hostBindingTarget) {
166
+ targetFactory(factory, this.hostBindingTarget, this._targets);
167
+ }
168
+ // If the binding has been targeted or it is a host binding and the view has a hostBindingTarget
169
+ if (factory.targetNodeId in this.targets) {
170
+ const behavior = factory.createBehavior();
171
+ behavior.bind(this);
172
+ behaviors[i] = behavior;
173
+ }
174
+ else {
175
+ let templateString = this.sourceTemplate.html;
176
+ if (typeof templateString !== "string") {
177
+ templateString = templateString.innerHTML;
178
+ }
179
+ const fragment = createRangeForNodes(this.firstChild, this.lastChild).cloneContents();
180
+ const result = getHydrationDiagnostic().formatBindingMismatch(factory, this.firstChild, this.lastChild, getHostName(this.firstChild));
181
+ throw new HydrationBindingError(result.message, factory, fragment, templateString, result.expected, result.received);
182
+ }
183
+ }
184
+ }
185
+ else {
186
+ if (this.source !== null) {
187
+ this.evaluateUnbindables();
188
+ }
189
+ this.isBound = false;
190
+ this.source = source;
191
+ this.context = context;
192
+ for (let i = 0, ii = behaviors.length; i < ii; ++i) {
193
+ behaviors[i].bind(this);
194
+ }
195
+ }
196
+ this.isBound = true;
197
+ this._hydrationStage = HydrationStage.hydrated;
198
+ }
199
+ unbind() {
200
+ if (!this.isBound || this.source === null) {
201
+ return;
202
+ }
203
+ this.evaluateUnbindables();
204
+ this.source = null;
205
+ this.context = this;
206
+ this.isBound = false;
207
+ }
208
+ /**
209
+ * Removes the view and unbinds its behaviors, disposing of DOM nodes afterward.
210
+ * Once a view has been disposed, it cannot be inserted or bound again.
211
+ */
212
+ dispose() {
213
+ removeNodeSequence(this.firstChild, this.lastChild);
214
+ this.unbind();
215
+ }
216
+ onUnbind(behavior) {
217
+ this.unbindables.push(behavior);
218
+ }
219
+ evaluateUnbindables() {
220
+ const unbindables = this.unbindables;
221
+ for (let i = 0, ii = unbindables.length; i < ii; ++i) {
222
+ unbindables[i].unbind(this);
223
+ }
224
+ unbindables.length = 0;
225
+ }
226
+ }
227
+ _a = Hydratable;
228
+ makeSerializationNoop(HydrationView);
@@ -4,11 +4,12 @@ import { oneTime } from "../binding/one-time.js";
4
4
  import { oneWay } from "../binding/one-way.js";
5
5
  import { FASTElementDefinition } from "../components/fast-definitions.js";
6
6
  import { isHydratable } from "../components/hydration.js";
7
- import { isFunction, isString } from "../interfaces.js";
7
+ import { isFunction, isString, Message } from "../interfaces.js";
8
+ import { FAST } from "../platform.js";
8
9
  import { HTMLDirective, } from "./html-directive.js";
10
+ import { HydrationStage } from "./hydration-view.js";
9
11
  import { Markup } from "./markup.js";
10
12
  import { html, ViewTemplate, } from "./template.js";
11
- import { HydrationStage } from "./view.js";
12
13
  /**
13
14
  * A Behavior that enables advanced rendering.
14
15
  * @public
@@ -45,11 +46,10 @@ export class RenderBehavior {
45
46
  if (viewNodes) {
46
47
  this.view = this.template.hydrate(viewNodes.first, viewNodes.last);
47
48
  this.bindView(this.view);
49
+ return;
48
50
  }
49
51
  }
50
- else {
51
- this.refreshView();
52
- }
52
+ this.refreshView();
53
53
  }
54
54
  /**
55
55
  * Unbinds this behavior.
@@ -172,6 +172,32 @@ function instructionToTemplate(def) {
172
172
  }
173
173
  return def.template;
174
174
  }
175
+ function resolveTemplateBindingValue(result, dataBinding, source, context) {
176
+ var _a;
177
+ if (isString(result)) {
178
+ return instructionToTemplate(getForInstance(dataBinding.evaluate(source, context), result));
179
+ }
180
+ if (result instanceof Node) {
181
+ return (_a = result.$fastTemplate) !== null && _a !== void 0 ? _a : new NodeTemplate(result);
182
+ }
183
+ return result;
184
+ }
185
+ function adaptTemplateBinding(binding, dataBinding) {
186
+ const evaluateTemplate = binding.evaluate;
187
+ const adapter = Object.create(Object.getPrototypeOf(binding));
188
+ for (const propertyName of Object.getOwnPropertyNames(binding)) {
189
+ if (propertyName !== "evaluate") {
190
+ Object.defineProperty(adapter, propertyName, Object.getOwnPropertyDescriptor(binding, propertyName));
191
+ }
192
+ }
193
+ Object.defineProperty(adapter, "evaluate", {
194
+ configurable: true,
195
+ enumerable: true,
196
+ value: (source, context) => resolveTemplateBindingValue(evaluateTemplate(source, context), dataBinding, source, context),
197
+ writable: true,
198
+ });
199
+ return adapter;
200
+ }
175
201
  function createElementTemplate(tagName, options) {
176
202
  const markup = [];
177
203
  const values = [];
@@ -251,6 +277,13 @@ function register(optionsOrInstruction) {
251
277
  const instruction = instanceOf(optionsOrInstruction)
252
278
  ? optionsOrInstruction
253
279
  : create(optionsOrInstruction);
280
+ if (lookup[instruction.name] !== void 0) {
281
+ const typeName = instruction.type.name || "(anonymous)";
282
+ FAST.warn(Message.duplicateRenderInstruction, {
283
+ type: typeName,
284
+ name: instruction.name,
285
+ });
286
+ }
254
287
  return (lookup[instruction.name] = instruction);
255
288
  }
256
289
  function getByType(type, name) {
@@ -425,19 +458,7 @@ export function render(value, template) {
425
458
  });
426
459
  }
427
460
  else if (template instanceof Binding) {
428
- const evaluateTemplate = template.evaluate;
429
- template.evaluate = (s, c) => {
430
- var _a;
431
- let result = evaluateTemplate(s, c);
432
- if (isString(result)) {
433
- result = instructionToTemplate(getForInstance(dataBinding.evaluate(s, c), result));
434
- }
435
- else if (result instanceof Node) {
436
- result = (_a = result.$fastTemplate) !== null && _a !== void 0 ? _a : new NodeTemplate(result);
437
- }
438
- return result;
439
- };
440
- templateBinding = template;
461
+ templateBinding = adaptTemplateBinding(template, dataBinding);
441
462
  }
442
463
  else {
443
464
  templateBinding = oneTime((s, c) => template);
@@ -1,11 +1,12 @@
1
1
  import { normalizeBinding } from "../binding/normalize.js";
2
2
  import { HydrationMarkup, isHydratable } from "../components/hydration.js";
3
- import { ArrayObserver, Sort, Splice } from "../observation/arrays.js";
3
+ import { ArrayObserver } from "../observation/arrays.js";
4
4
  import { Observable, } from "../observation/observable.js";
5
5
  import { emptyArray } from "../platform.js";
6
6
  import { HTMLDirective, } from "./html-directive.js";
7
+ import { HydrationStage } from "./hydration-view.js";
7
8
  import { Markup } from "./markup.js";
8
- import { HTMLView, HydrationStage, HydrationView } from "./view.js";
9
+ import { HTMLView } from "./view.js";
9
10
  const defaultRepeatOptions = Object.freeze({
10
11
  positioning: false,
11
12
  recycle: true,
@@ -25,6 +26,21 @@ function bindWithPositioning(view, items, index, controller) {
25
26
  function isCommentNode(node) {
26
27
  return node.nodeType === Node.COMMENT_NODE;
27
28
  }
29
+ function removeNodeRange(first, last) {
30
+ const parentNode = first.parentNode;
31
+ if (parentNode === null) {
32
+ return;
33
+ }
34
+ let current = first;
35
+ while (current !== null) {
36
+ const next = current.nextSibling;
37
+ parentNode.removeChild(current);
38
+ if (current === last) {
39
+ break;
40
+ }
41
+ current = next;
42
+ }
43
+ }
28
44
  export class HydrationRepeatError extends Error {
29
45
  constructor(
30
46
  /**
@@ -284,63 +300,83 @@ export class RepeatBehavior {
284
300
  }
285
301
  }
286
302
  hydrateViews(template) {
303
+ var _a;
287
304
  if (!this.items) {
288
305
  return;
289
306
  }
290
- this.views = new Array(this.items.length);
307
+ const items = this.items;
308
+ const itemCount = items.length;
309
+ const views = (this.views = new Array(itemCount));
310
+ // First pass: collect all repeat marker pairs by walking backward.
311
+ // Each entry tracks both the item content range and its SSR markers.
312
+ const itemRanges = [];
291
313
  let current = this.location.previousSibling;
292
314
  while (current !== null) {
293
- if (!isCommentNode(current)) {
294
- current = current.previousSibling;
295
- continue;
296
- }
297
- const index = HydrationMarkup.parseRepeatEndMarker(current.data);
298
- if (index === null) {
315
+ if (!isCommentNode(current) ||
316
+ !HydrationMarkup.isRepeatViewEndMarker(current.data)) {
299
317
  current = current.previousSibling;
300
318
  continue;
301
319
  }
302
- current.data = "";
303
- // end of repeat is the previousSibling of end comment
304
- const end = current.previousSibling;
320
+ const endMarker = current;
321
+ endMarker.data = "";
322
+ const end = endMarker.previousSibling;
305
323
  if (!end) {
306
324
  throw new Error(`Error when hydrating inside "${this.location.getRootNode().host.nodeName}": end should never be null.`);
307
325
  }
308
- // find start marker
326
+ // Find matching start marker via balanced counting
309
327
  let start = end;
310
- // How many unmatched end markers we've encountered
311
- let unmatchedEndMarkers = 0;
328
+ let depth = 0;
312
329
  while (start !== null) {
313
330
  if (isCommentNode(start)) {
314
331
  if (HydrationMarkup.isRepeatViewEndMarker(start.data)) {
315
- unmatchedEndMarkers++;
332
+ depth++;
316
333
  }
317
334
  else if (HydrationMarkup.isRepeatViewStartMarker(start.data)) {
318
- if (unmatchedEndMarkers) {
319
- unmatchedEndMarkers--;
320
- }
321
- else {
322
- if (HydrationMarkup.parseRepeatStartMarker(start.data) !==
323
- index) {
324
- throw new Error(`Error when hydrating inside "${this.location.getRootNode().host
325
- .nodeName}": Mismatched start and end markers.`);
326
- }
327
- start.data = "";
328
- current = start.previousSibling;
329
- // start of repeat content is the nextSibling of start comment
330
- start = start.nextSibling;
331
- const view = template.hydrate(start, end);
332
- this.views[index] = view;
333
- this.bindView(view, this.items, index, this.controller);
335
+ if (depth === 0) {
336
+ const startMarker = start;
337
+ startMarker.data = "";
338
+ current = startMarker.previousSibling;
339
+ const itemStart = (_a = startMarker.nextSibling) !== null && _a !== void 0 ? _a : endMarker;
340
+ // Empty item: start and end markers are adjacent.
341
+ const itemEnd = end === startMarker ? itemStart : end;
342
+ itemRanges.push({
343
+ start: itemStart,
344
+ end: itemEnd,
345
+ startMarker,
346
+ endMarker,
347
+ });
334
348
  break;
335
349
  }
350
+ depth--;
336
351
  }
337
352
  }
338
353
  start = start.previousSibling;
339
354
  }
340
355
  if (!start) {
341
- throw new Error(`Error when hydrating inside "${this.location.getRootNode().host.nodeName}": start should never be null.`);
356
+ throw new Error(`Error when hydrating inside "${this.location.getRootNode().host.nodeName}": repeat start marker not found.`);
342
357
  }
343
358
  }
359
+ // Ranges were collected backward (last item first).
360
+ // Reverse so index 0 = first SSR item.
361
+ itemRanges.reverse();
362
+ // Hydrate each SSR item at its correct index (0-based from start).
363
+ const hydrationCount = Math.min(itemRanges.length, itemCount);
364
+ for (let i = 0; i < hydrationCount; i++) {
365
+ const { start, end } = itemRanges[i];
366
+ const view = template.hydrate(start, end);
367
+ views[i] = view;
368
+ this.bindView(view, items, i, this.controller);
369
+ }
370
+ for (let i = hydrationCount; i < itemCount; i++) {
371
+ const view = template.create();
372
+ views[i] = view;
373
+ this.bindView(view, items, i, this.controller);
374
+ view.insertBefore(this.location);
375
+ }
376
+ for (let i = itemCount, ii = itemRanges.length; i < ii; i++) {
377
+ const { startMarker, endMarker } = itemRanges[i];
378
+ removeNodeRange(startMarker, endMarker);
379
+ }
344
380
  }
345
381
  }
346
382
  /**
@@ -77,13 +77,6 @@ export class ViewTemplate {
77
77
  }
78
78
  return this.result;
79
79
  }
80
- /**
81
- * Creates an HTMLView instance based on this template definition.
82
- * @param hostBindingTarget - The element that host behaviors will be bound to.
83
- */
84
- create(hostBindingTarget) {
85
- return this.compile().createView(hostBindingTarget);
86
- }
87
80
  /**
88
81
  * Returns a directive that can inline the template.
89
82
  */
@@ -121,6 +114,13 @@ export class ViewTemplate {
121
114
  view.appendTo(host);
122
115
  return view;
123
116
  }
117
+ /**
118
+ * Creates an HTMLView instance based on this template definition.
119
+ * @param hostBindingTarget - The element that host behaviors will be bound to.
120
+ */
121
+ create(hostBindingTarget) {
122
+ return this.compile().createView(hostBindingTarget);
123
+ }
124
124
  /**
125
125
  * Processes the tagged template literal's static strings and interpolated values and
126
126
  * creates a ViewTemplate.