@microsoft/fast-element 2.10.2 → 2.10.3

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 (36) hide show
  1. package/ARCHITECTURE_FASTELEMENT.md +1 -1
  2. package/ARCHITECTURE_HTML_TAGGED_TEMPLATE_LITERAL.md +3 -1
  3. package/ARCHITECTURE_INTRO.md +1 -1
  4. package/ARCHITECTURE_OVERVIEW.md +1 -1
  5. package/CHANGELOG.json +108 -1
  6. package/CHANGELOG.md +10 -2
  7. package/DESIGN.md +506 -0
  8. package/biome.json +4 -0
  9. package/dist/context/context.api.json +17 -1
  10. package/dist/di/di.api.json +17 -1
  11. package/dist/dts/hydration/target-builder.d.ts +17 -1
  12. package/dist/dts/templating/html-binding-directive.d.ts +39 -4
  13. package/dist/dts/templating/html-directive.d.ts +7 -1
  14. package/dist/dts/templating/template.d.ts +19 -1
  15. package/dist/dts/templating/view.d.ts +11 -0
  16. package/dist/dts/tsdoc-metadata.json +1 -1
  17. package/dist/esm/components/hydration.js +17 -0
  18. package/dist/esm/hydration/target-builder.js +22 -1
  19. package/dist/esm/templating/compiler.js +29 -11
  20. package/dist/esm/templating/html-binding-directive.js +59 -4
  21. package/dist/esm/templating/html-directive.js +7 -1
  22. package/dist/esm/templating/install-hydratable-view-templates.js +6 -0
  23. package/dist/esm/templating/markup.js +8 -0
  24. package/dist/esm/templating/template.js +19 -1
  25. package/dist/esm/templating/view.js +13 -0
  26. package/dist/fast-element.api.json +20 -4
  27. package/dist/fast-element.debug.js +176 -227
  28. package/dist/fast-element.debug.min.js +2 -2
  29. package/dist/fast-element.js +176 -227
  30. package/dist/fast-element.min.js +2 -2
  31. package/dist/fast-element.untrimmed.d.ts +76 -6
  32. package/docs/api-report.api.md +3 -3
  33. package/package.json +7 -19
  34. package/tsconfig.api-extractor.json +6 -0
  35. package/.eslintrc.json +0 -19
  36. package/karma.conf.cjs +0 -148
@@ -143,7 +143,13 @@ export declare const HTMLDirective: Readonly<{
143
143
  */
144
144
  define<TType extends Constructable<HTMLDirective>>(type: TType, options?: PartialHTMLDirectiveDefinition): TType;
145
145
  /**
146
- *
146
+ * Determines the DOM aspect type for a directive based on attribute name prefix.
147
+ * The prefix convention maps to aspect types as follows:
148
+ * - No prefix (e.g. "class") → DOMAspect.attribute
149
+ * - ":" prefix (e.g. ":value") → DOMAspect.property (":classList" → DOMAspect.tokenList)
150
+ * - "?" prefix (e.g. "?disabled") → DOMAspect.booleanAttribute
151
+ * - `@` prefix (e.g. `@click`) → DOMAspect.event
152
+ * - Falsy or absent value → DOMAspect.content (see remarks)
147
153
  * @param directive - The directive to assign the aspect to.
148
154
  * @param value - The value to base the aspect determination on.
149
155
  * @remarks
@@ -142,7 +142,25 @@ export declare class ViewTemplate<TSource = any, TParent = any> implements Eleme
142
142
  */
143
143
  render(source: TSource, host: Node, hostBindingTarget?: Element): HTMLView<TSource, TParent>;
144
144
  /**
145
- * Creates a template based on a set of static strings and dynamic values.
145
+ * Processes the tagged template literal's static strings and interpolated values and
146
+ * creates a ViewTemplate.
147
+ *
148
+ * For each interpolated value:
149
+ * 1. Functions (binding expressions, e.g., `x => x.name`) → wrapped in a one-way HTMLBindingDirective
150
+ * 2. Binding instances → wrapped in an HTMLBindingDirective
151
+ * 3. HTMLDirective instances → used as-is
152
+ * 4. Static values (strings, numbers) → wrapped in a one-time HTMLBindingDirective
153
+ *
154
+ * Each directive's createHTML() is called with an `add` callback that registers
155
+ * the factory in the factories record under a unique ID and returns that ID.
156
+ * The directive inserts a placeholder marker (e.g., `fast-abc123{0}fast-abc123`) into
157
+ * the HTML string so the compiler can later find and associate it with the factory.
158
+ *
159
+ * Aspect detection happens here too: the `lastAttributeNameRegex` checks whether
160
+ * the placeholder appears inside an attribute value, and if so, assignAspect()
161
+ * sets the correct DOMAspect (attribute, property, event, etc.) based on the
162
+ * attribute name prefix.
163
+ *
146
164
  * @param strings - The static strings to create the template with.
147
165
  * @param values - The dynamic values to create the template with.
148
166
  * @param policy - The DOMPolicy to associated with the template.
@@ -197,6 +197,17 @@ export declare class HTMLView<TSource = any, TParent = any> extends DefaultExecu
197
197
  }): void;
198
198
  /**
199
199
  * Binds a view's behaviors to its binding source.
200
+ *
201
+ * On the first call, this iterates through all compiled factories, calling
202
+ * createBehavior() on each to produce a ViewBehavior instance (e.g., an
203
+ * HTMLBindingDirective), and then immediately binds it. This is where event
204
+ * listeners are registered, expression observers are created, and initial
205
+ * DOM values are set.
206
+ *
207
+ * On subsequent calls with a new source, existing behaviors are re-bound
208
+ * to the new data source, which re-evaluates all binding expressions and
209
+ * updates the DOM accordingly.
210
+ *
200
211
  * @param source - The binding source for the view's binding behaviors.
201
212
  * @param context - The execution context to run the behaviors within.
202
213
  */
@@ -5,7 +5,7 @@
5
5
  "toolPackages": [
6
6
  {
7
7
  "packageName": "@microsoft/api-extractor",
8
- "packageVersion": "7.47.0"
8
+ "packageVersion": "7.57.7"
9
9
  }
10
10
  ]
11
11
  }
@@ -1,4 +1,21 @@
1
1
  import { FAST } from "../platform.js";
2
+ /**
3
+ * Regex patterns for parsing hydration markers embedded as HTML comments by the SSR renderer.
4
+ * Each marker type encodes factory indices so the client can map markers back to ViewBehaviorFactories.
5
+ *
6
+ * Content binding markers bracket text/template content:
7
+ * <!-- fe-b$$start$$<factoryIndex>$$<uniqueId>$$fe-b -->
8
+ * ...content...
9
+ * <!-- fe-b$$end$$<factoryIndex>$$<uniqueId>$$fe-b -->
10
+ *
11
+ * Repeat markers bracket each repeated item:
12
+ * <!-- fe-repeat$$start$$<itemIndex>$$fe-repeat -->
13
+ * <!-- fe-repeat$$end$$<itemIndex>$$fe-repeat -->
14
+ *
15
+ * Element boundary markers demarcate nested custom elements so parent walkers can skip them:
16
+ * <!-- fe-eb$$start$$<elementId>$$fe-eb -->
17
+ * <!-- fe-eb$$end$$<elementId>$$fe-eb -->
18
+ */
2
19
  const bindingStartMarker = /fe-b\$\$start\$\$(\d+)\$\$(.+)\$\$fe-b/;
3
20
  const bindingEndMarker = /fe-b\$\$end\$\$(\d+)\$\$(.+)\$\$fe-b/;
4
21
  const repeatViewStartMarker = /fe-repeat\$\$start\$\$(\d+)\$\$fe-repeat/;
@@ -43,7 +43,23 @@ function isShadowRoot(node) {
43
43
  return node instanceof DocumentFragment && "mode" in node;
44
44
  }
45
45
  /**
46
- * Maps {@link CompiledViewBehaviorFactory} ids to the corresponding node targets for the view.
46
+ * Maps compiled ViewBehaviorFactory IDs to their corresponding DOM nodes in the
47
+ * server-rendered shadow root. Uses a TreeWalker to scan the existing DOM between
48
+ * firstNode and lastNode, parsing hydration markers to build the targets map.
49
+ *
50
+ * For element nodes: parses `data-fe-b` (or variant) attributes to identify which
51
+ * factories target each element, then removes the marker attribute.
52
+ *
53
+ * For comment nodes: parses content binding markers (`fe-b$$start/end$$`) to find
54
+ * the DOM range controlled by each content binding. Single text nodes become the
55
+ * direct target; multi-node ranges are stored in boundaries for structural directives.
56
+ * Element boundary markers (`fe-eb$$start/end$$`) cause the walker to skip over
57
+ * nested custom elements that handle their own hydration.
58
+ *
59
+ * Host bindings (targetNodeId='h') appear at the start of the factories array but
60
+ * have no SSR markers — getHydrationIndexOffset() computes how many to skip so that
61
+ * marker indices align with the correct non-host factories.
62
+ *
47
63
  * @param firstNode - The first node of the view.
48
64
  * @param lastNode - The last node of the view.
49
65
  * @param factories - The Compiled View Behavior Factories that belong to the view.
@@ -169,6 +185,11 @@ function skipToElementBoundaryEndMarker(node, walker) {
169
185
  current = walker.nextSibling();
170
186
  }
171
187
  }
188
+ /**
189
+ * Counts how many factories at the start of the array are host bindings (targetNodeId='h').
190
+ * Host bindings target the custom element itself and are not represented by SSR markers,
191
+ * so the marker indices must be offset by this count to align with the correct factory.
192
+ */
172
193
  function getHydrationIndexOffset(factories) {
173
194
  let offset = 0;
174
195
  for (let i = 0, ii = factories.length; i < ii; ++i) {
@@ -7,6 +7,12 @@ import { HTMLBindingDirective } from "./html-binding-directive.js";
7
7
  import { HTMLDirective, } from "./html-directive.js";
8
8
  import { nextId, Parser } from "./markup.js";
9
9
  import { HTMLView } from "./view.js";
10
+ /**
11
+ * Builds a hierarchical node ID by appending the child index to the parent's ID.
12
+ * For example, the third child of root is "r.2", and its first child is "r.2.0".
13
+ * These IDs are used as property names on the targets prototype so that each
14
+ * binding's target DOM node can be lazily resolved via a chain of childNodes lookups.
15
+ */
10
16
  const targetIdFrom = (parentId, nodeIndex) => `${parentId}.${nodeIndex}`;
11
17
  const descriptorCache = {};
12
18
  // used to prevent creating lots of objects just to track node and index while compiling
@@ -56,6 +62,13 @@ class CompilationContext {
56
62
  this.proto = Object.create(null, this.descriptors);
57
63
  return this;
58
64
  }
65
+ /**
66
+ * Registers a lazy getter on the targets prototype that resolves a DOM node
67
+ * by navigating from its parent's childNodes at the given index. Getters are
68
+ * chained: accessing targets["r.0.2"] first resolves targets["r.0"] (which
69
+ * resolves targets["r"]), then returns childNodes[2]. Results are cached so
70
+ * each node is resolved at most once per view instance.
71
+ */
59
72
  addTargetDescriptor(parentId, targetId, targetIndex) {
60
73
  const descriptors = this.descriptors;
61
74
  if (targetId === "r" || // root
@@ -66,7 +79,7 @@ class CompilationContext {
66
79
  if (!descriptors[parentId]) {
67
80
  const index = parentId.lastIndexOf(".");
68
81
  const grandparentId = parentId.substring(0, index);
69
- const childIndex = parseInt(parentId.substring(index + 1));
82
+ const childIndex = parseInt(parentId.substring(index + 1), 10);
70
83
  this.addTargetDescriptor(grandparentId, parentId, childIndex);
71
84
  }
72
85
  let descriptor = descriptorCache[targetId];
@@ -81,13 +94,20 @@ class CompilationContext {
81
94
  }
82
95
  descriptors[targetId] = descriptor;
83
96
  }
97
+ /**
98
+ * Creates a new HTMLView by cloning the compiled DocumentFragment and building
99
+ * a targets object. The targets prototype contains lazy getters that resolve
100
+ * each binding's target DOM node via childNodes traversal. Accessing every
101
+ * registered nodeId eagerly triggers the getter chain so all nodes are resolved
102
+ * before behaviors are bound.
103
+ */
84
104
  createView(hostBindingTarget) {
85
105
  const fragment = this.fragment.cloneNode(true);
86
106
  const targets = Object.create(this.proto);
87
- targets.r = fragment;
88
- targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : warningHost;
107
+ targets.r = fragment; // root — the cloned DocumentFragment
108
+ targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : warningHost; // host — the custom element
89
109
  for (const id of this.nodeIds) {
90
- targets[id]; // trigger locator
110
+ Reflect.get(targets, id); // trigger lazy getter to resolve and cache the DOM node
91
111
  }
92
112
  return new HTMLView(fragment, this.factories, targets);
93
113
  }
@@ -107,7 +127,6 @@ function compileAttributes(context, parentId, node, nodeId, nodeIndex, includeBa
107
127
  }
108
128
  }
109
129
  else {
110
- /* eslint-disable-next-line @typescript-eslint/no-use-before-define */
111
130
  result = Compiler.aggregate(parseResult, context.policy);
112
131
  }
113
132
  if (result !== null) {
@@ -152,7 +171,6 @@ function compileChildren(context, parent, parentId) {
152
171
  let nodeIndex = 0;
153
172
  let childNode = parent.firstChild;
154
173
  while (childNode) {
155
- /* eslint-disable-next-line @typescript-eslint/no-use-before-define */
156
174
  const result = compileNode(context, parentId, childNode, nodeIndex);
157
175
  childNode = result.node;
158
176
  nodeIndex = result.index;
@@ -167,14 +185,14 @@ function compileNode(context, parentId, node, nodeIndex) {
167
185
  break;
168
186
  case 3: // text node
169
187
  return compileContent(context, node, parentId, nodeId, nodeIndex);
170
- case 8: // comment
188
+ case 8: {
189
+ // comment
171
190
  const parts = Parser.parse(node.data, context.directives);
172
191
  if (parts !== null) {
173
- context.addFactory(
174
- /* eslint-disable-next-line @typescript-eslint/no-use-before-define */
175
- Compiler.aggregate(parts), parentId, nodeId, nodeIndex, null);
192
+ context.addFactory(Compiler.aggregate(parts), parentId, nodeId, nodeIndex, null);
176
193
  }
177
194
  break;
195
+ }
178
196
  }
179
197
  next.index = nodeIndex + 1;
180
198
  next.node = node.nextSibling;
@@ -182,7 +200,7 @@ function compileNode(context, parentId, node, nodeIndex) {
182
200
  }
183
201
  function isMarker(node, directives) {
184
202
  return (node &&
185
- node.nodeType == 8 &&
203
+ node.nodeType === 8 &&
186
204
  Parser.parse(node.data, directives) !== null);
187
205
  }
188
206
  const templateTag = "TEMPLATE";
@@ -9,6 +9,14 @@ import { HydrationStage } from "./view.js";
9
9
  function isContentTemplate(value) {
10
10
  return value.create !== undefined;
11
11
  }
12
+ /**
13
+ * Sink function for DOMAspect.content bindings (text content interpolation).
14
+ * Handles two cases:
15
+ * - If the value is a ContentTemplate (has a create() method), it composes a child
16
+ * view into the DOM, managing view lifecycle (create/reuse/remove/bind).
17
+ * - If the value is a primitive, it sets target.textContent directly, first removing
18
+ * any previously composed view.
19
+ */
12
20
  function updateContent(target, aspect, value, controller) {
13
21
  // If there's no actual value, then this equates to the
14
22
  // empty string for the purposes of content bindings.
@@ -77,6 +85,12 @@ function updateContent(target, aspect, value, controller) {
77
85
  target.textContent = value;
78
86
  }
79
87
  }
88
+ /**
89
+ * Sink function for DOMAspect.tokenList bindings (e.g., :classList).
90
+ * Uses a versioning scheme to efficiently track which CSS classes were added
91
+ * in the current update vs. the previous one. Classes from the previous version
92
+ * that aren't present in the new value are automatically removed.
93
+ */
80
94
  function updateTokenList(target, aspect, value) {
81
95
  var _a;
82
96
  const lookup = `${this.id}-t`;
@@ -109,6 +123,12 @@ function updateTokenList(target, aspect, value) {
109
123
  }
110
124
  }
111
125
  }
126
+ /**
127
+ * Maps each DOMAspect type to its corresponding DOM update ("sink") function.
128
+ * When a binding value changes, the sink function for the binding's aspect type
129
+ * is called to push the new value into the DOM. Events are handled separately
130
+ * via addEventListener in bind(), so the event sink is a no-op.
131
+ */
112
132
  const sinkLookup = {
113
133
  [DOMAspect.attribute]: DOM.setAttribute,
114
134
  [DOMAspect.booleanAttribute]: DOM.setBooleanAttribute,
@@ -118,7 +138,18 @@ const sinkLookup = {
118
138
  [DOMAspect.event]: () => void 0,
119
139
  };
120
140
  /**
121
- * A directive that applies bindings.
141
+ * The central binding directive that bridges data expressions and DOM updates.
142
+ *
143
+ * HTMLBindingDirective fulfills three roles simultaneously:
144
+ * - **HTMLDirective**: Produces placeholder HTML via createHTML() during template authoring.
145
+ * - **ViewBehaviorFactory**: Creates behaviors (returns itself) during view creation.
146
+ * - **ViewBehavior / EventListener**: Attaches to a DOM node during bind, manages
147
+ * expression observers for reactive updates, and handles DOM events directly.
148
+ *
149
+ * The aspectType (set by HTMLDirective.assignAspect during template processing)
150
+ * determines which DOM "sink" function is used to apply values — e.g.,
151
+ * setAttribute for attributes, addEventListener for events, textContent for content.
152
+ *
122
153
  * @public
123
154
  */
124
155
  export class HTMLBindingDirective {
@@ -157,7 +188,18 @@ export class HTMLBindingDirective {
157
188
  }
158
189
  return this;
159
190
  }
160
- /** @internal */
191
+ /**
192
+ * Attaches this binding to its target DOM node.
193
+ * - For events: stores the controller reference on the target element and registers
194
+ * this directive as the EventListener via addEventListener. The directive's
195
+ * handleEvent() method will be called when the event fires.
196
+ * - For content bindings: registers an unbind handler, then falls through to the
197
+ * default path.
198
+ * - For all non-event bindings: creates (or reuses) an ExpressionObserver, evaluates
199
+ * the binding expression, and applies the result to the DOM via the updateTarget
200
+ * sink function. The observer will call handleChange() on future data changes.
201
+ * @internal
202
+ */
161
203
  bind(controller) {
162
204
  var _a;
163
205
  const target = controller.targets[this.targetNodeId];
@@ -196,7 +238,14 @@ export class HTMLBindingDirective {
196
238
  view.needsBindOnly = true;
197
239
  }
198
240
  }
199
- /** @internal */
241
+ /**
242
+ * Implements the EventListener interface. When a DOM event fires on the target
243
+ * element, this method retrieves the ViewController stored on the element,
244
+ * sets the event on the ExecutionContext so `c.event` is available to the
245
+ * binding expression, and evaluates the expression. If the expression returns
246
+ * anything other than `true`, the event's default action is prevented.
247
+ * @internal
248
+ */
200
249
  handleEvent(event) {
201
250
  const controller = event.currentTarget[this.data];
202
251
  if (controller.isBound) {
@@ -208,7 +257,13 @@ export class HTMLBindingDirective {
208
257
  }
209
258
  }
210
259
  }
211
- /** @internal */
260
+ /**
261
+ * Called by the ExpressionObserver when a tracked dependency changes.
262
+ * Re-evaluates the binding expression via observer.bind() and pushes
263
+ * the new value to the DOM through the updateTarget sink function.
264
+ * This is the reactive update path that keeps the DOM in sync with data.
265
+ * @internal
266
+ */
212
267
  handleChange(binding, observer) {
213
268
  const target = observer.target;
214
269
  const controller = observer.controller;
@@ -29,7 +29,13 @@ export const HTMLDirective = Object.freeze({
29
29
  return type;
30
30
  },
31
31
  /**
32
- *
32
+ * Determines the DOM aspect type for a directive based on attribute name prefix.
33
+ * The prefix convention maps to aspect types as follows:
34
+ * - No prefix (e.g. "class") → DOMAspect.attribute
35
+ * - ":" prefix (e.g. ":value") → DOMAspect.property (":classList" → DOMAspect.tokenList)
36
+ * - "?" prefix (e.g. "?disabled") → DOMAspect.booleanAttribute
37
+ * - `@` prefix (e.g. `@click`) → DOMAspect.event
38
+ * - Falsy or absent value → DOMAspect.content (see remarks)
33
39
  * @param directive - The directive to assign the aspect to.
34
40
  * @param value - The value to base the aspect determination on.
35
41
  * @remarks
@@ -5,6 +5,12 @@ import { HydrationView } from "./view.js";
5
5
  // and a hydrate method. Augmenting the hydration features is done by
6
6
  // property assignment instead of class extension to better allow the
7
7
  // hydration feature to be tree-shaken.
8
+ //
9
+ // When hydrate() is called, it creates a HydrationView that wraps the
10
+ // pre-rendered DOM range (firstChild → lastChild) instead of cloning a
11
+ // compiled DocumentFragment. The HydrationView will then use
12
+ // buildViewBindingTargets() to scan for hydration markers and attach
13
+ // reactive bindings to the existing DOM nodes.
8
14
  Object.defineProperties(ViewTemplate.prototype, {
9
15
  [Hydratable]: { value: Hydratable, enumerable: false, configurable: false },
10
16
  hydrate: {
@@ -1,3 +1,9 @@
1
+ /**
2
+ * A unique per-session random marker string used to create placeholder tokens in HTML.
3
+ * Bindings embedded in template literals are replaced with interpolation markers
4
+ * of the form `fast-xxxxxx{id}fast-xxxxxx` so the compiler can later locate them in the
5
+ * parsed DOM and associate each marker with its ViewBehaviorFactory.
6
+ */
1
7
  const marker = `fast-${Math.random().toString(36).substring(2, 8)}`;
2
8
  const interpolationStart = `${marker}{`;
3
9
  const interpolationEnd = `}${marker}`;
@@ -49,6 +55,8 @@ export const Parser = Object.freeze({
49
55
  * directives or null if no directives are found in the string.
50
56
  */
51
57
  parse(value, factories) {
58
+ // Split on the interpolation start marker. If there's only one part,
59
+ // no placeholders exist and we return null to signal "no directives here."
52
60
  const parts = value.split(interpolationStart);
53
61
  if (parts.length === 1) {
54
62
  return null;
@@ -122,7 +122,25 @@ export class ViewTemplate {
122
122
  return view;
123
123
  }
124
124
  /**
125
- * Creates a template based on a set of static strings and dynamic values.
125
+ * Processes the tagged template literal's static strings and interpolated values and
126
+ * creates a ViewTemplate.
127
+ *
128
+ * For each interpolated value:
129
+ * 1. Functions (binding expressions, e.g., `x => x.name`) → wrapped in a one-way HTMLBindingDirective
130
+ * 2. Binding instances → wrapped in an HTMLBindingDirective
131
+ * 3. HTMLDirective instances → used as-is
132
+ * 4. Static values (strings, numbers) → wrapped in a one-time HTMLBindingDirective
133
+ *
134
+ * Each directive's createHTML() is called with an `add` callback that registers
135
+ * the factory in the factories record under a unique ID and returns that ID.
136
+ * The directive inserts a placeholder marker (e.g., `fast-abc123{0}fast-abc123`) into
137
+ * the HTML string so the compiler can later find and associate it with the factory.
138
+ *
139
+ * Aspect detection happens here too: the `lastAttributeNameRegex` checks whether
140
+ * the placeholder appears inside an attribute value, and if so, assignAspect()
141
+ * sets the correct DOMAspect (attribute, property, event, etc.) based on the
142
+ * attribute name prefix.
143
+ *
126
144
  * @param strings - The static strings to create the template with.
127
145
  * @param values - The dynamic values to create the template with.
128
146
  * @param policy - The DOMPolicy to associated with the template.
@@ -177,6 +177,17 @@ export class HTMLView extends DefaultExecutionContext {
177
177
  }
178
178
  /**
179
179
  * Binds a view's behaviors to its binding source.
180
+ *
181
+ * On the first call, this iterates through all compiled factories, calling
182
+ * createBehavior() on each to produce a ViewBehavior instance (e.g., an
183
+ * HTMLBindingDirective), and then immediately binds it. This is where event
184
+ * listeners are registered, expression observers are created, and initial
185
+ * DOM values are set.
186
+ *
187
+ * On subsequent calls with a new source, existing behaviors are re-bound
188
+ * to the new data source, which re-evaluates all binding expressions and
189
+ * updates the DOM accordingly.
190
+ *
180
191
  * @param source - The binding source for the view's binding behaviors.
181
192
  * @param context - The execution context to run the behaviors within.
182
193
  */
@@ -186,6 +197,8 @@ export class HTMLView extends DefaultExecutionContext {
186
197
  }
187
198
  let behaviors = this.behaviors;
188
199
  if (behaviors === null) {
200
+ // First bind: create behaviors from factories and bind each one.
201
+ // The view (this) acts as the ViewController, providing targets and source.
189
202
  this.source = source;
190
203
  this.context = context;
191
204
  this.behaviors = behaviors = new Array(this.factories.length);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "metadata": {
3
3
  "toolPackage": "@microsoft/api-extractor",
4
- "toolVersion": "7.47.0",
4
+ "toolVersion": "7.57.7",
5
5
  "schemaVersion": 1011,
6
6
  "oldestForwardsCompatibleVersion": 1001,
7
7
  "tsdocConfig": {
@@ -114,6 +114,22 @@
114
114
  "tagName": "@virtual",
115
115
  "syntaxKind": "modifier"
116
116
  },
117
+ {
118
+ "tagName": "@jsx",
119
+ "syntaxKind": "block"
120
+ },
121
+ {
122
+ "tagName": "@jsxRuntime",
123
+ "syntaxKind": "block"
124
+ },
125
+ {
126
+ "tagName": "@jsxFrag",
127
+ "syntaxKind": "block"
128
+ },
129
+ {
130
+ "tagName": "@jsxImportSource",
131
+ "syntaxKind": "block"
132
+ },
117
133
  {
118
134
  "tagName": "@betaDocumentation",
119
135
  "syntaxKind": "modifier"
@@ -10541,7 +10557,7 @@
10541
10557
  {
10542
10558
  "kind": "Class",
10543
10559
  "canonicalReference": "@microsoft/fast-element!HTMLBindingDirective:class",
10544
- "docComment": "/**\n * A directive that applies bindings.\n *\n * @public\n */\n",
10560
+ "docComment": "/**\n * The central binding directive that bridges data expressions and DOM updates.\n *\n * HTMLBindingDirective fulfills three roles simultaneously: - **HTMLDirective**: Produces placeholder HTML via createHTML() during template authoring. - **ViewBehaviorFactory**: Creates behaviors (returns itself) during view creation. - **ViewBehavior / EventListener**: Attaches to a DOM node during bind, manages expression observers for reactive updates, and handles DOM events directly.\n *\n * The aspectType (set by HTMLDirective.assignAspect during template processing) determines which DOM \"sink\" function is used to apply values — e.g., setAttribute for attributes, addEventListener for events, textContent for content.\n *\n * @public\n */\n",
10545
10561
  "excerptTokens": [
10546
10562
  {
10547
10563
  "kind": "Content",
@@ -11807,7 +11823,7 @@
11807
11823
  {
11808
11824
  "kind": "Method",
11809
11825
  "canonicalReference": "@microsoft/fast-element!HTMLView#bind:member(1)",
11810
- "docComment": "/**\n * Binds a view's behaviors to its binding source.\n *\n * @param source - The binding source for the view's binding behaviors.\n *\n * @param context - The execution context to run the behaviors within.\n */\n",
11826
+ "docComment": "/**\n * Binds a view's behaviors to its binding source.\n *\n * On the first call, this iterates through all compiled factories, calling createBehavior() on each to produce a ViewBehavior instance (e.g., an HTMLBindingDirective), and then immediately binds it. This is where event listeners are registered, expression observers are created, and initial DOM values are set.\n *\n * On subsequent calls with a new source, existing behaviors are re-bound to the new data source, which re-evaluates all binding expressions and updates the DOM accordingly.\n *\n * @param source - The binding source for the view's binding behaviors.\n *\n * @param context - The execution context to run the behaviors within.\n */\n",
11811
11827
  "excerptTokens": [
11812
11828
  {
11813
11829
  "kind": "Content",
@@ -23074,7 +23090,7 @@
23074
23090
  {
23075
23091
  "kind": "Method",
23076
23092
  "canonicalReference": "@microsoft/fast-element!ViewTemplate.create:member(1)",
23077
- "docComment": "/**\n * Creates a template based on a set of static strings and dynamic values.\n *\n * @remarks\n *\n * This API should not be used directly under normal circumstances because constructing a template in this way, if not done properly, can open up the application to XSS attacks. When using this API, provide a strong DOMPolicy that can properly sanitize and also be sure to manually sanitize all static strings particularly if they can come from user input.\n *\n * @param strings - The static strings to create the template with.\n *\n * @param values - The dynamic values to create the template with.\n *\n * @param policy - The DOMPolicy to associated with the template.\n *\n * @returns A ViewTemplate.\n */\n",
23093
+ "docComment": "/**\n * Processes the tagged template literal's static strings and interpolated values and creates a ViewTemplate.\n *\n * For each interpolated value: 1. Functions (binding expressions, e.g., `x => x.name`) → wrapped in a one-way HTMLBindingDirective 2. Binding instances → wrapped in an HTMLBindingDirective 3. HTMLDirective instances → used as-is 4. Static values (strings, numbers) → wrapped in a one-time HTMLBindingDirective\n *\n * Each directive's createHTML() is called with an `add` callback that registers the factory in the factories record under a unique ID and returns that ID. The directive inserts a placeholder marker (e.g., `fast-abc123{0}fast-abc123`) into the HTML string so the compiler can later find and associate it with the factory.\n *\n * Aspect detection happens here too: the `lastAttributeNameRegex` checks whether the placeholder appears inside an attribute value, and if so, assignAspect() sets the correct DOMAspect (attribute, property, event, etc.) based on the attribute name prefix.\n *\n * @remarks\n *\n * This API should not be used directly under normal circumstances because constructing a template in this way, if not done properly, can open up the application to XSS attacks. When using this API, provide a strong DOMPolicy that can properly sanitize and also be sure to manually sanitize all static strings particularly if they can come from user input.\n *\n * @param strings - The static strings to create the template with.\n *\n * @param values - The dynamic values to create the template with.\n *\n * @param policy - The DOMPolicy to associated with the template.\n *\n * @returns A ViewTemplate.\n */\n",
23078
23094
  "excerptTokens": [
23079
23095
  {
23080
23096
  "kind": "Content",